一棵樹,有點權和邊權。
你需要對這棵樹進行操作,每次選擇一條路徑,代價為路徑兩端的點權和,將路徑上的邊權全部減一。
問最小代價。
支援路徑邊權加操作。
\(n\le 2*10^5,q\le 2*10^5\)
dyp對noip2018d1t1的魔改。
%%%dyp
考慮乙個節點的貢獻。那麼相當於這樣的問題:一堆數\(a_i\),每次選擇\(x\neq y,a_x,a_y>0\),將\(a_x,a_y\)分別減一。問最後剩下的數最少是多少。
結論:如果\(2\max a_i> \sum a_i\),則答案為\(2\max a_i -\sum a_i\)。否則為\(\sum a_i\mod 2\)。
證明考慮假設操作到最後只剩\(a_i\),假設\(a_i\ge 2\),如果在之前的操作中,存在\((a_j,a_k)\),那麼調整成\((a_j,a_i),(a_k,a_i)\)顯然更優,所以前面的操作都是\((a_x,a_i)\)的形式。在這種情況下,\(a_i\)取一開始的最大值是最優的,此時滿足\(2\max a_i -\sum a_i>0\)。如果不滿足,那麼最後操作剩下的數根據總和的奇偶性判定。
如果\(\sum a_i\mod 2=1\),不妨一開始給\(\max a_i\)減一。然後後面再進行判斷。
於是貢獻為:\(\max(2\max a_i-\sum a_i,0)*w\)。
對於一次修改,兩端的點暴力做,考慮中間的點:如果改的兩條邊中,其中一條為過半邊,此時\(2(a_i+\delta)-(\sum a_i+2\delta)>0\),貢獻不變;如果兩條都不是過半邊,那麼貢獻減\(2\delta\),和\(0\)取\(\max\)。
那麼可以發現:對於中間的點,貢獻是遞減的,除非它成為了一次修改的端點,否則它的貢獻不可能增加。於是可以找到即將變成\(0\)的貢獻,暴力修改。一次修改操作多\(2\)的勢能,所以總勢能為\(o(n+q)\)。
總結一下:先發現結論,然後分開處理端點和中間的點,中間的點可以快速改;端點增加勢能,中間的點減勢能,時間複雜度是對的。
後面要做的就是樹剖維護這個東西,按套路搞即可,不再贅述。
因為懶得寫所以把std貼在這裡。
#include#include#include#include#define maxn 200005
#define inf 1e18
#define ll long long
using namespace std;
int n,q,i,j,k,fai[maxn];
int em,e[maxn*2],nx[maxn*2],ls[maxn],ec[maxn*2];
ll w[maxn];
void read(int &x)
void insert(int x,int y,int z)
//shu pou
int tot,dfn[maxn],idfn[maxn],sz[maxn],g[maxn],top[maxn],dep[maxn],fa[maxn];
void dfs(int x,int p)
}void dfs2(int x,int p)
int getlca(int x,int y) else v[x]=inf; }}
// sum
struct treearray
ll sum(int x,ll s=0)
ll query(int l,int r)
} t1,t2;
void addchain(int x,int y,int z,int d)
int mid=(l+r)>>1;
maketree(x<<1,l,mid),maketree(x<<1^1,mid+1,r);
upd(x);
}void cover(int x,int l,int r,int d)
int mid=(l+r)>>1;
cover(x<<1,l,mid,d),cover(x<<1^1,mid+1,r,d);
upd(x);
}void add(int x,int l,int r,int l,int r,int d)
void clear(int x,int l,int r,int v)
int mid=(l+r)>>1;
if (v<=mid) clear(x<<1,l,mid,v);
else clear(x<<1^1,mid+1,r,v);
upd(x);
}void addit(int x,int l,int r,int v,ll s)
int mid=(l+r)>>1;
if (v<=mid) addit(x<<1,l,mid,v,s);
else addit(x<<1^1,mid+1,r,v,s);
upd(x);
}void check(int x,int y,int d)
ll getit(int x,int l,int r,int v)
void doit(int x,int z,int d)
x=fa[x];
} if (dfn[z]+1tmp0) s=p[x]; else s=k,tmp=tmp0;
} else s=k,tmp=tmp0;
ll sum0=gets(x)+d; ans+=(sum0&1)*w[x];
if (tmp*2-sum0-(sum0&1)>0) else p[x]=0;
}int main()
dfs(1,0),dfs2(1,0);
prepare();
printf("%lld\n",ans);
maketree(1,1,n);
while (q--)
}
提高組(計數)
題目鏈結 類題 氣泡排序 求長度為 n 的排列中滿足最長下降子串行長度不超過 2 且符合 p x y 的排列數。n le 10 7,t le 10 6 題意轉化 不存在三個點,使得左邊的點比中間大,右邊的點比中間小。我們要知道乙個 trick 從大到小 從小到大列舉數,嘗試將其插入當前排列,並使之合...
2018 07 08 2018提高組 模擬C組
fj準備教他的奶牛彈奏一首歌曲,歌曲由n 1 n 50,000 種音節組成,編號為1到n,而且一定按照從1到n的順序進行彈奏,第i種音節持續b i 1 b i 10,000 個節拍,節拍從0開始計數,因此從節拍0到節拍b 1 1彈奏的是第1種音節,從b 1到b 1 b 2 1彈奏的是第2種音節,依此...
NOIP提高組 矩陣
在麥克雷的面前出現了乙個有n m個格仔的矩陣,每個格仔用 或 表示,表示這個格仔可以放東西,則表示這個格仔不能放東西。現在他拿著一條1 2大小的木棒,好奇的他想知道對於一些子矩陣,有多少種放木棒的方案。因為棍子是1 2的,所以很容易就能發現,兩個被分割的塊,除了跨越兩個塊擺放木棍的方案數會對答案有影...