• 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('
    ');}
    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;
    }
  • 相关阅读:
    HTTP状态码
    CentOS 7 上安装vim(默认未安装)
    yum安装提示Another app is currently holding the yum lock; waiting for it to exit...
    CentOS 7 安装telnet服务
    shell编程
    shell基础
    ssh相关命令
    ssh无密码连接
    centos7小命令
    日志管理
  • 原文地址:https://www.cnblogs.com/DreamlessDreams/p/10265689.html
Copyright © 2020-2023  润新知