P8047题解

P8047 [COCI2015-2016#4] GALAKSIJA

题目传送门

题解

显然是要删边变加边的,然后联通性也是显然要用并查集维护的,就是路径异或和需要一个数据结构来维护。

发现:加边删边不影响两个点的路径异或和。所以我们可以处理出每个点到 \(1\) 号节点的路径异或和 \(d\),于是 \(Path_{u,v}=d_u\operatorname{xor}d_v\),我们需要支持的就是查询在两个集合中分别取一个点使其 \(d\) 相等的方案数,用 map 启发式合并即可。由于复杂度瓶颈不在合并而在查询,所以可以直接把其中一个 map 所有点丢到另一个里,手写合并也不会更优。

时间复杂度 \(O(n\log^2n)\)

代码:

/*
 * @Author: operator_ 
 * @Date: 2024-01-09 12:45:43 
 * @Last Modified by: operator_
 * @Last Modified time: 2024-01-09 13:18:26
 */
#include<bits/stdc++.h>
using namespace std;
#define int long long
inline int rd() {
    int s=0,m=0;char ch=getchar();
    while(!isdigit(ch)) {if(ch=='-')m=1;ch=getchar();}
    while( isdigit(ch)) s=(s<<3)+(s<<1)+(ch^48),ch=getchar();
    return m?-s:s;
}
int n,u[100005],v[100005],w[100005],p[100005],d[100005],Ans[100005];
vector<int> g[100005],gw[100005];
void dfs(int u,int fa) {
    for(int i=0;i<g[u].size();i++) if(g[u][i]!=fa) 
        d[g[u][i]]=d[u]^gw[u][i],dfs(g[u][i],u);
}
int f[100005];map<int,int> mp[100005];
int Find(int x) {return f[x]==x?x:f[x]=Find(f[x]);}
signed main(){
    cin>>n;
    for(int i=1;i<n;i++) {
        u[i]=rd(),v[i]=rd(),w[i]=rd();
        g[u[i]].push_back(v[i]),gw[u[i]].push_back(w[i]);
        g[v[i]].push_back(u[i]),gw[v[i]].push_back(w[i]);
    }
    dfs(1,0);
    for(int i=1;i<n;i++) p[i]=rd();
    for(int i=1;i<=n;i++) f[i]=i,mp[i][d[i]]++;
    for(int i=n-1;i;i--) {
        Ans[i]=Ans[i+1];
        int x=Find(u[p[i]]),y=Find(v[p[i]]);
        if(mp[x].size()>mp[y].size()) swap(x,y);f[x]=y;
        for(auto it=mp[x].begin();it!=mp[x].end();it++)
            Ans[i]+=mp[y][it->first]*it->second,mp[y][it->first]+=it->second;
    }
    for(int i=1;i<=n;i++)
        printf("%lld\n",Ans[i]);
    return 0;
}
posted @ 2024-01-19 15:32  operator-  阅读(43)  评论(0)    收藏  举报