转载 原文链接hdu 5571
题意:
给出一个n个结点的树,有点权a[x]和边权val(x,y);
现有m次修改某个点的点权;
求每次操作后它所有结点对(x,y)之间的(a[x] xor a[y])*dis(x,y);
其中dis(x,y)指两点间距离;
n<=30000,m<=30000;
题解:
感觉上周的BC好奇怪啊。。。A上来就高精度没模板打个卵,B题SB题;
写完AB感觉药丸于是果断去看D,仔细一看卧槽。。。于是颓了一个小时C没做出来跪了;
感觉这道题的思路还是挺显然的,然而动态点分治我真的码不出来啊QAQ;
虽说如此我也没看懂题解,最后还是orz wyfcyx大爷涨了个新姿势才会做的。。
首先异或这东西在这里没什么好用的性质,我们只能把它拆开看了;
拆成每一位之后问题转化成了树上每个点有黑白两种颜色,动态维护不同颜色点对的总距离(我觉得不动态我还是能写出来的);
动态维护点分治的话,我们考虑每个点分治子树内跨过根的答案;
比如我现在修改的点是x,那么跨过根到x的答案和就是x到根的长度*对面的不同色点个数+对面不同色点到根的距离和;
因为到这里的信息是可减的,于是维护这些信息就可以了:分治结构中两种颜色点个数,两种颜色到根的点距离和,两种颜色到根在分治结构中父亲的距离和;
注意后两个都要开long long;
然后每次修改将当前点的答案都减去,修改之后再加上就可以了;
时间复杂度是O((n+m)*logn*14);
我还是too naive,被D动态点分治做的少啦。。。(那我也不做)
代码:
#include<queue>
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#define N 31000
#define M 14
using namespace std;
typedef long long ll;
int nxt[N<<1],to[N<<1],val[N<<1],head[N],ce;
int a[N],fa[N],deep[N],fv[N][16],rt;
ll ans;
bool ban[N];
int in[N];
queue<int>q;
struct Tree
{
int size[N][2];
ll f[N][2],g[N][2];
ll calc(int x,bool col)
{
ll ret=f[x][!col];
for(int y=x;fa[y];y=fa[y])
ret+=(ll)(size[fa[y]][!col]-size[y][!col])*(fv[x][deep[fa[y]]])+
f[fa[y]][!col]-g[y][!col];
return ret;
}
}tr[M];
void init()
{
ce=0;
ans=0;
memset(head,0,sizeof(head));
memset(ban,0,sizeof(ban));
memset(fv,0,sizeof(fv));
memset(tr,0,sizeof(tr));
}
void add(int x,int y,int v)
{
to[++ce]=y;
val[ce]=v;
nxt[ce]=head[x];
head[x]=ce;
}
int getG(int x,int pre,int &g,int n)
{
int temp,ma,size,i;
for(i=head[x],ma=0,size=1;i;i=nxt[i])
{
if(!ban[to[i]]&&to[i]!=pre)
{
temp=getG(to[i],x,g,n);
ma=max(temp,ma);
size+=temp;
}
}
ma=max(ma,n-size);
if(ma<=n>>1)
g=x;
return size;
}
void dfs(int x,int pre,int dis,int dp)
{
fv[x][dp]=dis;
for(int i=head[x];i;i=nxt[i])
{
if(!ban[to[i]]&&to[i]!=pre)
{
dfs(to[i],x,dis+val[i],dp);
}
}
}
void Build(int x,int n)
{
ban[x]=1;
if(n==1)
return ;
int temp,i,g;
dfs(x,0,0,deep[x]);
for(i=head[x];i;i=nxt[i])
{
if(!ban[to[i]])
{
temp=getG(to[i],0,g,0);
getG(to[i],0,g,temp);
deep[g]=deep[x]+1;
fa[g]=x;
Build(g,temp);
}
}
}
void update(int x,int y)
{
int i,j,k;
for(i=0;i<M;i++)
{
ans-=(1ll<<i)*tr[i].calc(x,a[x]>>i&1);
}
for(i=0;i<M;i++)
for(j=x;j;j=fa[j])
{
tr[i].size[j][a[x]>>i&1]--;
tr[i].f[j][a[x]>>i&1]-=fv[x][deep[j]];
tr[i].g[j][a[x]>>i&1]-=fv[x][deep[fa[j]]];
}
a[x]=y;
for(i=0;i<M;i++)
for(j=x;j;j=fa[j])
{
tr[i].size[j][a[x]>>i&1]++;
tr[i].f[j][a[x]>>i&1]+=fv[x][deep[j]];
tr[i].g[j][a[x]>>i&1]+=fv[x][deep[fa[j]]];
}
for(i=0;i<M;i++)
{
ans+=(1ll<<i)*tr[i].calc(x,a[x]>>i&1);
}
}
int main()
{
int n,m,i,j,k,x,y,v;
while(scanf("%d",&n)!=EOF)
{
init();
for(i=1;i<=n;i++)
{
scanf("%d",a+i);
}
for(i=1;i<n;i++)
{
scanf("%d%d%d",&x,&y,&v);
add(x,y,v);
add(y,x,v);
}
getG(1,0,rt,n);
deep[rt]=fa[rt]=0;
Build(rt,n);
for(i=1;i<=n;i++)
{
in[fa[i]]++;
for(j=0;j<M;j++)
{
for(k=i;fa[k];k=fa[k])
{
tr[j].g[k][a[i]>>j&1]+=fv[i][deep[fa[k]]];
}
}
}
for(i=1;i<=n;i++)
if(!in[i])
q.push(i);
while(!q.empty())
{
x=q.front(),q.pop();
for(i=0;i<M;i++)
{
tr[i].size[x][a[x]>>i&1]++;
tr[i].size[fa[x]][0]+=tr[i].size[x][0];
tr[i].size[fa[x]][1]+=tr[i].size[x][1];
tr[i].f[fa[x]][0]+=tr[i].g[x][0];
tr[i].f[fa[x]][1]+=tr[i].g[x][1];
}
in[fa[x]]--;
if(!in[fa[x]])
q.push(fa[x]);
}
for(i=1;i<=n;i++)
{
for(j=0;j<M;j++)
{
ans+=(1ll<<j)*tr[j].calc(i,a[i]>>j&1);
}
}
ans>>=1;
scanf("%d",&m);
for(i=1;i<=m;i++)
{
scanf("%d%d",&x,&y);
update(x,y);
cout<<ans<<endl;
}
}
return 0;
}
原文链接hdu 5571