题意
有一棵\(LCT\),给出每个点的\(access\)次数,求一个最优的\(access\)操作顺序使得重轻链切换的次数最多。
sol
像我这种外省酱油选手在考场上当然只有10分暴力分
有没有人像我一样在考场上想到[SDOI2017]树点涂色然而依然没有一点思路的?
emmmmmm...
分别考虑每个点的贡献。
对于一个点来说,如果我们希望这个点的贡献尽量大,那么就需要尽量让每棵子树交替进行\(access\)。这个时候需要讨论一下:(记点\(i\)的\(access\)次数为\(a_i\),点\(i\)子树中的\(a_i\)之和为\(size_i\))
1、如果\(2*a_i>size_i\)则说明子树中的\(access\)次数不能完全和\(i\)点交替,此时\(i\)点的贡献为\(2*(size_i-a_i)\);
2、如果存在\(v\)是\(i\)的儿子满足\(2*size_v>size_i\)(显然这样的\(v\)至多只有一个),那么说明子树\(v\)以外的点的\(access\)次数不能完全和子树\(v\)匹配,此时\(i\)点的贡献为\(2*(size_i-size_v)\);
3、若以上两者均不满足,则说明子树\(i\)中的\(access\)次数可以完全匹配,此时\(i\)点的贡献为\(size_i-1\)。
在上述2、中提到了对于每个\(i\)而言,满足\(2*size_v>size_i\)的\(v\)至多只有一个。类比于\(LCT\)中每个点的重儿子至多只有一个,我们把那个满足条件的\(v\)当做点\(i\)的重儿子用\(LCT\)维护即可。
每次修改一个点的\(a_i\)相当于要修改整条路径上的\(size_i\),可以类似\(LCT\)中的\(access\)的写法,对于每一次跳到的\(x\)做一次左儿子(也就是上方路径)的路径加,以及讨论一下\(x\)的重儿子的变换情况。
复杂度不会证。据说是\(O(n\log{n})\)。
code
#include<cstdio> #include<algorithm> using namespace std; #define ll long long int gi() { int x=0,w=1;char ch=getchar(); while ((ch<'0'||ch>'9')&&ch!='-') ch=getchar(); if (ch=='-') w=0,ch=getchar(); while (ch>='0'&&ch<='9') x=(x<<3)+(x<<1)+ch-'0',ch=getchar(); return w?x:-x; } const int N = 4e5+5; int n,m,to[N<<1],nxt[N<<1],head[N],cnt,fa[N],ch[2][N],son[N]; ll a[N],size[N],tag[N],f[N],ans; void link(int u,int v) { to[++cnt]=v;nxt[cnt]=head[u]; head[u]=cnt; } void update(int u) { ans-=f[u];f[u]=size[u]-1; if (son[u]) f[u]=(size[u]-size[son[u]])*2; if (a[u]*2>size[u]) f[u]=(size[u]-a[u])*2; ans+=f[u]; } void dfs(int u,int ff) { size[u]=a[u];fa[u]=ff; for (int e=head[u];e;e=nxt[e]) if (to[e]!=ff) dfs(to[e],u),size[u]+=size[to[e]]; for (int e=head[u];e;e=nxt[e]) if (to[e]!=ff) if (size[to[e]]*2>size[u]) son[u]=ch[1][u]=to[e]; update(u); } bool Son(int x) { return ch[1][fa[x]]==x; } bool isroot(int x) { return ch[0][fa[x]]!=x&&ch[1][fa[x]]!=x; } void cover(int x,ll v) { if (!x) return; size[x]+=v;tag[x]+=v; } void pushdown(int x) { if (!tag[x]) return; cover(ch[0][x],tag[x]);cover(ch[1][x],tag[x]); tag[x]=0; } void alldown(int x) { if (!isroot(x)) alldown(fa[x]); pushdown(x); } void rotate(int x) { int y=fa[x],z=fa[y],c=Son(x); ch[c][y]=ch[c^1][x];if (ch[c][y]) fa[ch[c][y]]=y; fa[x]=z;if (!isroot(y)) ch[Son(y)][z]=x; ch[c^1][x]=y;fa[y]=x; } void splay(int x) { alldown(x); for (int y=fa[x];!isroot(x);rotate(x),y=fa[x]) if (!isroot(y)) Son(x)^Son(y)?rotate(x):rotate(y); } int findroot(int x) { while (ch[0][x]) pushdown(x),x=ch[0][x]; return x; } void access(int x,int v) { a[x]+=v; for (int y=0;x;y=x,x=fa[x]) { splay(x);size[x]+=v;cover(ch[0][x],v); if (son[x]) { alldown(son[x]); if (size[son[x]]*2<=size[x]) son[x]=ch[1][x]=0; } int gg=findroot(y); if (size[gg]*2>size[x]) son[x]=gg,ch[1][x]=y; update(x); } } int main() { n=gi();m=gi(); for (int i=1;i<=n;++i) a[i]=gi(); for (int i=1;i<n;++i) { int u=gi(),v=gi(); link(u,v);link(v,u); } dfs(1,0);printf("%lld\n",ans); while (m--) { int x=gi(),y=gi(); access(x,y); printf("%lld\n",ans); } return 0; }
来源:https://www.cnblogs.com/zhoushuyu/p/8672671.html