LOJ #6509. 「雅礼集训 2018 Day7」C
神仙题
题意
给定一棵树,点权为0/1,每次随机一个点(可能和之前所在点相同)走到该点并将其点权异或上1
求期望的移动距离使得所有点点权相同
题解
根本不会解方程
容易发现如果一个点不是最后一次被走到,就会随机下一个点并走过去
即如果我们能求出每个点非最后一次走到的期望次数,就可以算出答案
由于完全随机,初始相同颜色的点非最后一次走到的次数相同
设$ f_{i,0/1}$表示在有$ i$个1的时候,0/1非最后一次走到的期望次数
很艰难的列出方程如下
$$ f_{i,0} = \frac{i}{n} f_{i-1, 0} + \frac{n-i-1}{n} f_{i+1, 0} + \frac{1}{n} f_{i+1,1} + \frac{1}{n} $$
$$ f_{i,1} = \frac{n-i}{n} f_{i+1, 1} + \frac{i-1}{n} f_{i-1, 1} + \frac{1}{n} f_{i-1,0} + \frac{1}{n}$$
注意由于记录的是非最后一次走到,需要特判边界
即当前已经有$ n-1$个黑色时将最后一个白点翻成黑点不算入期望次数,黑点翻成白点同理
然后可以暴力高消了
但这个方程有很多优美的性质
不容易发现$ f_{i+1,1}$只和$ f_{i,1}$和$ f_{i-1,0/1}$有关
因此如果知道前面的信息就可以推出$ f_{i+1,1}$
同理再借助$ f_{i+1,1}$就能推出$ f_{i+1,0}$
因此我们设$ f_{1,0}=x,f_{1,1}=y$并把所有$ f_{x,0/1}$用$ Ax+By+c$表示
然后在最后联立两个方程解出$ x,y$就做完了
边界细节真**多
代码
#include<ctime> #include<cmath> #include<cstdio> #include<cstring> #include<iostream> #include<algorithm> #include<queue> #include<vector> #define p 1000000007 #define rt register int #define ll long long using namespace std; inline ll read(){ ll x=0;char zf=1;char ch=getchar(); while(ch!='-'&&!isdigit(ch))ch=getchar(); if(ch=='-')zf=-1,ch=getchar(); while(isdigit(ch))x=x*10+ch-'0',ch=getchar();return x*zf; } void write(ll y){if(y<0)putchar('-'),y=-y;if(y>9)write(y/10);putchar(y%10+48);} void writeln(const ll y){write(y);putchar('\n');} int k,m,n,x,y,z,cnt; struct fc{ ll a,b,c;//ax+by+c fc operator +(const fc s)const{return {a+s.a,b+s.b,c+s.c};} fc operator -(const fc s)const{return {a-s.a,b-s.b,c-s.c};} fc operator *(const int s)const{return {a*s%p,b*s%p,c*s%p};} fc operator +(const int s)const{return {a,b,c+s};} fc operator -(const int s)const{return {a,b,c-s};} }f[100010][2]; int inv[100010],fa[100010],size[100010];ll ans[100010]; char c[100010]; vector<int>E[100010]; int ksm(int x,int y=p-2){ x%=p; while(x<0)x+=p; int ans=1; for(rt i=y;i;i>>=1,x=1ll*x*x%p)if(i&1)ans=1ll*ans*x%p; return ans; } void dfs(int x){ size[x]=1; for(auto i:E[x]){ dfs(i); size[x]+=size[i]; ans[x]+=ans[i]+size[i]; } } void dfs2(int x){ if(x!=1)ans[x]=ans[fa[x]]+n-size[x]*2; for(auto i:E[x])dfs2(i); } int main(){ n=read();scanf("%s",c+1);int sum=0; for(rt i=1;i<=n;i++)sum+=(c[i]=='1'); inv[0]=inv[1]=1; for(rt i=2;i<=n;i++)inv[i]=1ll*inv[p%i]*(p-p/i)%p; f[1][0].a=1;f[1][1].b=1; for(rt i=1;i<=n-2;i++){ if(i==1)f[i+1][1]=(f[i][1]*n-f[i-1][1]*(i-1)-f[i-1][0])*inv[n-i]; else f[i+1][1]=(f[i][1]*n-f[i-1][1]*(i-1)-f[i-1][0]-1)*inv[n-i]; f[i+1][0]=(f[i][0]*n-f[i-1][0]*i-f[i+1][1]-1)*inv[n-i-1]; } fc x=f[n][0]; fc a0=f[n-2][0]*(n-1)-f[n-1][0]*n,a1=f[n-2][1]*(n-2)-f[n-1][1]*n+f[n-2][0]+1; ll a=a0.a,b=a0.b,C=a0.c,d=a1.a,e=a1.b,F=a1.c; int invd=ksm(d); int Y=(F%p*invd%p*a%p-C)%p*ksm(b%p-e%p*invd%p*a%p)%p; int X=(b%p*Y%p+C%p)*ksm(a)%p;X=-X; int B=(f[sum][1].a*X+f[sum][1].b*Y+f[sum][1].c)%p+inv[n]; int W=(f[sum][0].a*X+f[sum][0].b*Y+f[sum][0].c)%p+inv[n]; B%=p;W%=p;B=1ll*B*inv[n]%p,W=1ll*W*inv[n]%p; for(rt i=2;i<=n;i++){ fa[i]=read(); E[fa[i]].push_back(i); } dfs(1);dfs2(1);ll ret=0; for(rt i=1;i<=n;i++)if(c[i]=='1')(ret+=1ll*B*(ans[i]%p)%p)%=p; else (ret+=1ll*W*(ans[i]%p)%p)%=p; cout<<(ret+p)%p; return 0; }