题意: 给定一棵树 当前树的答案为 $f[1]^f[2]^f[3]^..^f[n]$ f[i]表示去除掉i点 该树的联通块数量 有m次操作 每次将两个点连一条边 然后再输出该树的答案 题目 题解: 显然一开始的时候 答
题意: 给定一棵树 当前树的答案为 $f[1]^f[2]^f[3]^..^f[n]$ f[i]表示去除掉i点 该树的联通块数量
有m次操作 每次将两个点连一条边 然后再输出该树的答案
题目
题解: 显然一开始的时候 答案为每个点的答案为其度 所以可以处理好一开始的答案
如果将两个点连在一起的时候 那么该路径所经过的点(不包括这两个端点) 的答案都会减一
但是考虑到有时候会重复更新 可以将每个点转移到他到儿子的边上 显然 他有多少个儿子就可以被减多少次答案 正好匹配上了
用并查集来维护点的归属 所以每条边只会被遍历一次 那么复杂度就是 均摊了复杂度m
比赛的时候被lca卡了 写的是树剖lca 其实可以暴力预处理出所有的lca
#include<bits/stdc++.h> using namespace std; const int N=5e3+10; int f[N],n,a,b,x[2],y[2],z[2],deg[N],ans,m; int find1(int x){return f[x]==x?x:f[x]=find1(f[x]);} int dep[N],fa[N],siz[N],pre[N],id[N],lca[N][N],pos,ncnt,head[N]; struct Edge{int to,nex;}edge[N<<1]; void add(int a,int b){edge[++pos]=(Edge){b,head[a]};head[a]=pos;} void dfs(int x,int f) { fa[x]=f;dep[x]=dep[f]+1;id[x]=++ncnt;pre[ncnt]=x;siz[x]=1;lca[x][x]=x; for(int i=head[x];i;i=edge[i].nex) { int v=edge[i].to; if(v==f)continue; dfs(v,x);siz[x]+=siz[v]; for(int j=id[x];j<id[v];j++) for(int k=id[v];k<id[v]+siz[v];k++) lca[pre[j]][pre[k]]=lca[pre[k]][pre[j]]=x; } } void solve(int x,int y) { x=find1(x); while(dep[x]>dep[y]+1) { ans^=deg[fa[x]]; deg[fa[x]]--; ans^=deg[fa[x]]; f[x]=fa[x]; x=find1(fa[x]); } } void init() { for(int i=0;i<=n;i++)f[i]=i,head[i]=0,deg[i]=0; ncnt=ans=pos=0; } int main() { while(scanf("%d%d%d%d%d%d",&n,&m,&a,&b,&x[0],&y[0])!=EOF) { int u,v; init(); for(int i=1;i<n;i++)scanf("%d%d",&u,&v),add(u,v),add(v,u),deg[u]++,deg[v]++; dfs(0,0); for(int i=0;i<n;i++)ans^=deg[i]; z[0]=ans; for(int i=1;i<=m;i++) { x[1]=(a*x[0]+b*y[0]+z[0])%n; y[1]=(b*x[0]+a*y[0]+z[0])%n; solve(x[1],lca[x[1]][y[1]]); z[0]=ans;x[0]=x[1];y[0]=y[1]; } printf("%d %d\n",x[1],y[1]); } return 0; }View Code