【ZJOI2018】历史【结论】【LCT思想】
題意:一棵nnn個點的樹,每個點有權值aia_iai?,初始時給定。維護:
單點權值加上一個正數。
詢問每個點恰好執行aia_iai?次 access 操作,任意安排順序的條件下切換輕重鏈總次數的最大值。
n≤4×105n\leq 4\times 10^5n≤4×105
一道奇怪的題(?)
對于一個點uuu,考慮它子樹內兩個相鄰 access的點,它們在uuu這里切換了一次當且僅當它們屬于 不同的 uuu的兒子 為根 的 子樹。(為了方便,我們把uuu單獨這一個點看成uuu的子樹。)
把來自同一個子樹內的一次access看成一種顏色的小球,我們希望相鄰的不同色小球盡量多。
結論 設總小球數為nnn,顏色最多的小球個數為mmm,那么最大貢獻為min?{n?1,2(n?m)}\min\{n-1,2(n-m)\}min{n?1,2(n?m)}
證明
考慮把最多的顏色擺出來,在m?1m-1m?1個空隙中放其他顏色。
把所有其他顏色的球依次往空隙中放,放完一個顏色繼續往下一個空隙放,空隙放完了再從第一個開始。
如果空隙都放完了,即n?m≥m?1n-m\geq m-1n?m≥m?1,即n?1≤2(n?m)n-1\leq2(n-m)n?1≤2(n?m),所有相鄰的小球顏色都不同,答案為n?1n-1n?1
否則顏色最多的球一定會相鄰。考慮開始時貢獻為000,插入一個任意顏色的球最多讓貢獻+2+2+2,上面的構造方法可以達到這個最大值,所以最大貢獻為2(n?m)2(n-m)2(n?m)
顯然這時m≤n2m\leq \frac{n}{2}m≤2n?,把最多的顏色各取一個構成一組綁在一起,然后其他顏色用同樣的方法放在組之間的空隙內,這樣不會有同色相鄰。
就可以O(n)O(n)O(n)處理了
然后考慮怎么用數據結構維護這玩意
上面已經推過了,雖然表達式看起來很奇怪,但實際上本質上是判斷最大的子樹是否超過自己的一半(這里的大小都是指aaa的和)
一點也不自然地想到實鏈剖分
具體而言,設sumusum_usumu?表示uuu子樹內權值和,對于uuu和它的兒子vvv,如果2sumv>sumu+12sum_v>sum_u+12sumv?>sumu?+1(先不管uuu本身什么的),那么vvv是uuu的重兒子,顯然重兒子最多有一個。如果不存在這樣的兒子就沒有重兒子。
這樣做的好處是重鏈上都是取的min?{n?1,2(n?m)}\min\{n-1,2(n-m)\}min{n?1,2(n?m)}的右邊這項,而把父親和兒子加上同一個正數時左邊會變大,右邊不會變,而右邊本來就比左邊小,不僅仍然是重邊,連這個點的答案都不會改變,直接跳過去就可以了。而到根的輕邊是O(log?V)O(\log V)O(logV)的,直接討論一下輕邊的情況即可。
具體而言就是魔改一下LCT的access,跳輕邊的時候需要判一下。具體實現的時候很詭異,建議直接看代碼。
注意復雜度是通過輕邊條數而非finger-search保證的,所以必須在修改前dfs一遍處理出重兒子
復雜度應該是O(nlog?nlog?V)O(n\log n\log V)O(nlognlogV)
#include <iostream> #include <cstdio> #include <cstring> #include <cctype> #include <vector> #define MAXN 400005 using namespace std; typedef long long ll; inline int read() {int ans=0;char c=getchar();while (!isdigit(c)) c=getchar();while (isdigit(c)) ans=(ans<<3)+(ans<<1)+(c^48),c=getchar();return ans; } int ch[MAXN][2],fa[MAXN]; ll val[MAXN],lz[MAXN]; inline void pushlzy(int x,ll v){val[x]+=v,lz[x]+=v;} inline void pushdown(int x) {if (lz[x]){if (ch[x][0]) pushlzy(ch[x][0],lz[x]);if (ch[x][1]) pushlzy(ch[x][1],lz[x]);lz[x]=0;} } inline int get(int x){return ch[fa[x]][1]==x;} inline bool isroot(int x){return ch[fa[x]][0]!=x&&ch[fa[x]][1]!=x;} inline void rotate(int x) {int y=fa[x],z=fa[y];int l=get(x),r=l^1;int w=ch[x][r];if (!isroot(y)) ch[z][get(y)]=x;ch[x][r]=y,ch[y][l]=w;if (w) fa[w]=y;fa[y]=x,fa[x]=z; } int q[MAXN],tp; inline void splay(int x) {q[tp=1]=x;for (int i=x;!isroot(i);i=fa[i]) q[++tp]=fa[i];for (int i=tp;i>=1;i--) pushdown(q[i]);while (!isroot(x)){int y=fa[x];if (!isroot(y)){if (get(x)==get(y)) rotate(y);else rotate(x);}rotate(x);} } ll res[MAXN],a[MAXN],ans; inline int findrt(int x){while (ch[x][0]) pushdown(x),x=ch[x][0];return x;} inline void access(int x,int v) {a[x]+=v;for (int y=0;x;y=x,x=fa[x]){splay(x);val[x]+=v;if (ch[x][0]) pushlzy(ch[x][0],v);ans-=res[x];int t=findrt(ch[x][1]);if (2*(val[x]-val[t])>=val[x]-1) ch[x][1]=0;t=findrt(y);if (2*(val[x]-val[t])<val[x]-1) ch[x][1]=y;t=findrt(ch[x][1]);res[x]=(t? 2*(val[x]-val[t]):val[x]-1);if (2*a[x]>val[x]+1) res[x]=2*(val[x]-a[x]);ans+=res[x];} } vector<int> e[MAXN]; void dfs(int u) {val[u]=a[u];for (int i=0;i<(int)e[u].size();i++)if (e[u][i]!=fa[u]){fa[e[u][i]]=u;dfs(e[u][i]);val[u]+=val[e[u][i]];}for (int i=0;i<(int)e[u].size();i++)if (e[u][i]!=fa[u]&&2*val[e[u][i]]>val[u]+1)ch[u][1]=e[u][i];res[u]=(ch[u][1]? 2*(val[u]-val[ch[u][1]]):val[u]-1);if (2*a[u]>val[u]+1) res[u]=2*(val[u]-a[u]);ans+=res[u]; } int main() {int n,m;n=read(),m=read();for (int i=1;i<=n;i++) a[i]=read();for (int i=1;i<n;i++){int u,v;u=read(),v=read();e[u].push_back(v),e[v].push_back(u);}dfs(1); printf("%lld\n",ans);while (m--){int x,v;x=read(),v=read();access(x,v);printf("%lld\n",ans);}return 0; }總結
以上是生活随笔為你收集整理的【ZJOI2018】历史【结论】【LCT思想】的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 不由自主的磕牙是什么病?
- 下一篇: 【UOJ188】 Sanrd【类min_