洛谷 P3359 改造异或树

传送门
30分做法:(边权为0)
我一开始就在想30分做法,然后就想到了:
首先老套路,正着删边就是倒着加边。
并查集维护连通块大小,每次加边后将两个端点的size值乘起来累加答案。
20分做法:
当n<=1000时,利用xor的一个性质:a^x^x=a;
然后就可以选取一个根,一遍dfs,预处理出来每个点到根节点的距离,这样dist[x,y]=s[x]^s[y];
可以每次修改后暴力查找,然后随便搞搞就好了。
50分做法:
结合做法1和做法2。
100分做法:
给每一个块都建一个平衡树(由于我太蒟蒻了,就不自己手写了,我就用map了,虽然map慢到爆炸,但也可以将就着用吧),在每一次修改的时候用启发式合并,一边合并一边统计答案,就完美解决了。
代码:(最慢的点400ms)

#include<map>
#include<cstdio>
#define ll long long
using namespace std;
inline int read(){
    int x=0;char ch=' ';
    while(ch<'0'||ch>'9')ch=getchar();
    while(ch>='0'&&ch<='9')x=(x<<3)+(x<<1)+ch-'0',ch=getchar();
    return x;
}
struct edge{
    int to,next,w;
}e[200001];
map<int,int> mp[100001];
map<int,int>::iterator it;
int n,tot,x[100001],y[100001],add[100001],fa[100001],z[100001],head[100001],s[100001];
ll sum[100001],ans;

int find(int x){return fa[x]==x?x:fa[x]=find(fa[x]);}
inline void addedge(int x,int y,int w){e[++tot].to=y;e[tot].next=head[x];e[tot].w=w;head[x]=tot;}
void dfs(int x,int fa){
    for(int i=head[x];i;i=e[i].next){
        int u=e[i].to;
        if(u!=fa){
            s[u]=s[x]^e[i].w;
            dfs(u,x);
        }
    }
}

int main(){
    n=read();
    for(int i=1;i<=n;i++)fa[i]=i;
    for(int i=1;i<n;i++){
        x[i]=read();y[i]=read();z[i]=read();
        addedge(x[i],y[i],z[i]);addedge(y[i],x[i],z[i]);
    }
    for(int i=1;i<n;i++){add[i]=read();}
    dfs(1,0);
    for(int i=1;i<=n;i++)mp[i][s[i]]=1;
    for(int i=n-1;i>=1;i--){
        int u=find(x[add[i]]);int v=find(y[add[i]]);
        if(mp[u].size()>mp[v].size())swap(u,v);
        fa[u]=v;
        for(it=mp[u].begin();it!=mp[u].end();++it){
            ans+=1ll*mp[v][it->first]*it->second;
            mp[v][it->first]+=it->second;
        }
        sum[i]=ans;
    }
    for(int i=1;i<=n;i++)printf("%lld
",sum[i]);
    return 0;
}
原文地址:https://www.cnblogs.com/stone41123/p/7581245.html