.jpg)
.jpg)
.jpg)
.jpg)
对于所有数据满足 1<=N<=50,000 1<=M<=50,000 1<=Ai<=10^6 1<=D<=100 1<=U,V<=N
题解:
给出一个无根树,维护四个操作,link,cut,路径加法,路径期望查询,前三个都是LCT的基本操作(LCT见前面的随笔),主要说第四个;
对于路径期望,如果两点之间的点数为n,那么坟墓很明显是C(n+1,2)=n*(n+1)/2;考虑维护分子:
如图所示,考虑每个点的贡献(小套路:当整体难以求出的时候,考虑求每个点的贡献,然后求和);第一个点对答案的贡献为a1*1*n(左端点只能选a1,右端点可以选a1~an),
第二个点对答案的贡献为a2*2*(n-1)(左端点可以选a1或a2,右端点可以选a2~an)
于是答案就是a1*1*n+a2*2*(n-1)+...+an*n*1 这个东西显然不能暴力求 需要在LCT上维护 但是直接维护根本维护不了 怎么办呢?
观察左子树 将左子树合并前后的贡献值作差可得

我们发现这个3其实就是右子树的size+1
于是我们们令lsum=1*a1+2*a2+3*a3...+n*an; rsum=n*a1+(n-1)*a2+...+1*an;
则:root.ans=lson.ans+lson.lsum*(rson.size+1) + rson.ans+rson.rsum*(lson.size+1) + root.sum*(lson.size+1)*(rson.size+1) (左子树贡献+右子树的贡献+根节点的贡献);
考虑对于lsum和rsum的维护,lsum和rsum都是加的val*root.size*(root.size+1)/2; 而ans却加val*(1*n+2*(n-1)+3*(n-2)+...+n*1)=n*(n+1)*(n+2)/6(这里的n=root.size)。
参考代码:
/**************************************************************
Problem: 3091
User: SongHL
Language: C++
Result: Accepted
Time:1764 ms
Memory:6464 kb
****************************************************************/
//BZOJ 3091
//给出一棵无根树,维护四个操作:link,cut,路径加法,路径期望查询
#include<bits/stdc++.h>
#define LL long long
#define inf 2147483640
#define Pi acos(-1.0)
using namespace std;
const int maxn=50010;
int n,m,fa[maxn];
struct node{
int son[2];
LL sum,tag,rev,ans,val,lsum,rsum,size;
int& operator [] (int x) {return son[x];}
}tree[maxn];
LL gcd(LL a,LL b) {return b==0 ? a : gcd(b,a%b);}
namespace LCT{
void reverse(int k)
{
swap(tree[k][0],tree[k][1]);
swap(tree[k].lsum,tree[k].rsum);
tree[k].rev^=1;
}
void add(int k,LL val)
{
tree[k].val+=val;
tree[k].sum+=tree[k].size*val;
tree[k].tag+=val;
tree[k].ans+=tree[k].size*(tree[k].size+1)*(tree[k].size+2)/6*val;
tree[k].lsum+=tree[k].size*(tree[k].size+1)/2*val;
tree[k].rsum+=tree[k].size*(tree[k].size+1)/2*val;
}
int find(int x) {return fa[x] ? find(fa[x]):x;}
void pushdown(int k)
{
if(tree[fa[k]][0]==k || tree[fa[k]][1]==k) pushdown(fa[k]);
int l=tree[k][0],r=tree[k][1];
if(tree[k].rev)
{
if(l) reverse(l);
if(r) reverse(r);
tree[k].rev^=1;
}
if(tree[k].tag)
{
if(l) add(l,tree[k].tag);
if(r) add(r,tree[k].tag);
tree[k].tag=0;
}
}
void pushup(int k)
{
int l=tree[k][0],r=tree[k][1];
tree[k].size=tree[l].size+tree[r].size+1;
tree[k].lsum=tree[l].lsum+(tree[l].size+1)*tree[k].val+tree[r].lsum+tree[r].sum*(tree[l].size+1);
tree[k].rsum=tree[r].rsum+(tree[r].size+1)*tree[k].val+tree[l].rsum+tree[l].sum*(tree[r].size+1);
tree[k].sum=tree[l].sum+tree[r].sum+tree[k].val;
tree[k].ans=tree[l].ans+tree[r].ans+tree[l].lsum*(tree[r].size+1)+tree[r].rsum*(tree[l].size+1)+tree[k].val*(tree[l].size+1)*(tree[r].size+1);
}
void rotate(int x)
{
int y=fa[x],z=fa[y],l,r;
l=tree[y][1]==x;r=l^1;
if(tree[z][0]==y || tree[z][1]==y) tree[z][tree[z][1]==y]=x;
fa[tree[x][r]]=y;fa[x]=z;fa[y]=x;
tree[y][l]=tree[x][r];tree[x][r]=y;
pushup(y);pushup(x);
}
void splay(int x)
{
pushdown(x);
while(tree[fa[x]][0]==x || tree[fa[x]][1]==x)
{
int y=fa[x],z=fa[y];
if(tree[z][0]==y || tree[z][1]==y)
{
if(tree[z][0]==y^tree[y][0]==x) rotate(x);
else rotate(y);
}
rotate(x);
}
}
void access(int x) //访问
{
for(int y=0;x;y=x,x=fa[x]) splay(x),tree[x][1]=y,pushup(x);
}
int findroot(int x)//找根(在真实的树中的)
{
access(x);splay(x);
while(tree[x][0]) pushdown(x),x=tree[x][0];
return x;
}
void makeroot(int x) //换根
{
access(x);splay(x);reverse(x);
}
void split(int x,int y)//提取路径
{
makeroot(x);
access(y);splay(y);
}
void link(int x,int y)
{
makeroot(x);fa[x]=y;
}
void cut(int x,int y)
{
makeroot(x);access(y);splay(y);
if(tree[y][0]==x && tree[x][1]==0)
tree[y][0]=0,fa[x]=0,pushup(y);
}
void modify(int x,int y,LL val)//对x~y这段路径操作
{
makeroot(x);access(y);
splay(y);add(y,val);
}
void query(int x,int y)
{
split(x,y);
LL A=tree[y].ans,B=tree[y].size*(tree[y].size+1)/2;
LL D=gcd(A,B);
printf("%lld/%lld\n",A/D,B/D);
}
}
using namespace LCT;
int main()
{
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++) scanf("%lld",&tree[i].val);
for(int u,v,i=1;i<n;i++)
{
scanf("%d%d",&u,&v);
link(u,v);
}
for(int op,x,y,d,i=1;i<=m;i++)
{
scanf("%d%d%d",&op,&x,&y);
if(op==1) if(find(x)==find(y)) cut(x,y);
if(op==2) if(find(x)!=find(y)) link(x,y);
if(op==3){ scanf("%d",&d); if(find(x)==find(y)) modify(x,y,d); }
if(op==4) if(find(x)==find(y)) query(x,y); else puts("-1");
}
return 0;
}
原文:https://www.cnblogs.com/songorz/p/9912208.html