『GROI-R1』继续深潜,为了同一个梦想 题解
验题人题解
考虑一个集合中一个点能产生贡献的情况。此时该点被集合包含,集合内的所有点在同一条树链上,且该集合中有至少两个点。
显然,一个点
由此延伸到一个点
考虑原题。对于结点
时间复杂度 优于 std。
参考代码
非唯一解法,仅供参考。
#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define _ 1000005
#define Mod 1000000007
void pls(ll &x,ll y){x=(x+y)%Mod;}
ll n,x,y,a[_],ans,s[_],cnt,hd[_];
//a[u]: 在以u为根的子树内,u必选,其余点可以全不选,
//且所有集合内的点都在同一条以u为一端的链上的方案数
struct o{ll nxt,to;}edg[_];
void add(ll x,ll y){edg[++cnt].nxt=hd[x];hd[x]=cnt;edg[cnt].to=y;}
//这里由于代码习惯,所有的u都写成了x,请见谅
void dfs1(ll x,ll fa){
a[x]=1;
for(ll i=hd[x];i;i=edg[i].nxt)if(edg[i].to!=fa){
dfs1(edg[i].to,x);
pls(s[x],(a[x]-1)*(a[edg[i].to]*2-1));//统计当前子树内部产生的贡献
pls(a[x],a[edg[i].to]*2-1);//更新当前子树下的方案数
}
}
void dfs2(ll x,ll fa,ll z){//z: 该子树外的方案数(限制同a[u])
pls(s[x],a[x]*z-1);//直接统计穿过子树内外的链产生的贡献
for(ll y,i=hd[x];i;i=edg[i].nxt)if(edg[i].to!=fa){
y=edg[i].to;
dfs2(y,x,((a[x]-a[y]*2+1+Mod*2)%Mod+z-1)*2%Mod);//更新
}
}
int main(){
ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);
cin>>n;
for(ll i=1;i<n;i++)cin>>x>>y,add(x,y),add(y,x);
dfs1(1,0);dfs2(1,0,1);
for(ll i=1;i<=n;i++)ans^=s[i]*i;
cout<<ans<<'\n';
}