LOJ #6509. 「雅礼集训 2018 Day7」C

神仙题

LOJ #6509


题意

给定一棵树,点权为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;
}

 

posted @ 2019-01-14 11:00  Kananix  阅读(678)  评论(0编辑  收藏  举报

Contact with me