zoukankan      html  css  js  c++  java
  • 「CEOI2020」星际迷航

    「CEOI2020」星际迷航

    首先是最简单的判断是否必胜的(dp)转移(displaystyle dp_u=igcup_{vin son_u} ext{not } dp_{v})

    考虑第(i+1)层对于第(i)层的贡献,实际上只和(i+1)层有多少个点(dp)值为0/1有关

    下面称(dp)值为0/1的点为 败/胜 点

    考虑对于确定第(i)层的根为某一节点(root)之后

    在某一个点下面接一个胜点,或者在一个胜点下面接一个点,对于胜败情况没有影响

    在一个败点下面接一个败点,可能会导致一段连续祖先段的胜败翻转

    考虑对于每个(u)作为根求出:

    1.没有接上下一层时的胜败情况 : (dp_u)

    2.有多少个节点接上一个败点之后,会导致根的胜败情况翻转:(R_u)

    这是一个换根(dp)问题

    具体来说,(R_u)的值,根据子节点中败点的个数可以得到转移:

    1.没有败点,那么就是所有子节点(R)之和+自己

    2.恰好有一个败点,那么就是败点的(R)

    对此,每个点维护子节点中败点的个数,然后换根(dp)即可

    求出每个(root)的1,2后,可以用一个矩阵维护每层的转移,就不再赘述

    #include<bits/stdc++.h>
    using namespace std;
    typedef long long ll;
    #define pb push_back
    #define rep(i,a,b) for(int i=a,i##end=b;i<=i##end;++i)
    #define drep(i,a,b) for(int i=a,i##end=b;i>=i##end;--i)
    
    char IO;
    int rd(){
        int s=0;
        while(!isdigit(IO=getchar()));
        do s=(s<<1)+(s<<3)+(IO^'0');
        while(isdigit(IO=getchar()));
        return s;
    }
    
    const int N=1e5+10,INF=1e9+10,P=1e9+7;
    
    int n;
    ll m;
    struct Mat{
        int a[2][2];
        void clear(){ memset(a,0,sizeof a); }
        Mat operator * (const Mat x) const {
            Mat res; res.clear();
            rep(i,0,1) rep(j,0,1) rep(k,0,1) res.a[i][k]=(res.a[i][k]+1ll*a[i][j]*x.a[j][k])%P;
            return res;
        }
    } Res,X;
    
    int F[N],FC[N],FR[N],son[N][2],SR[N];
    // F子树胜败,FC子节点败点个数,FR子树翻转点个数,son存储两个儿子中的败点,SR存储儿子中的FR之和
    int G[N],GC[N],GR[N];
    // F,GC,GR是子树外的
    int dp[N],R[N],fa[N];
    // dp,R即上文所述
    
    vector <int> E[N];
    void dfs1(int u,int f) {
        // 子树处理
        fa[u]=f;
        for(int v:E[u]) if(v!=f) {
            dfs1(v,u);
            FC[u]+=!F[v];
            if(!F[v]) {
                if(!son[u][0]) son[u][0]=v;
                else son[u][1]=v;
            }
            SR[u]+=FR[v];
        }
        F[u]=FC[u]>0;
        FR[u]=!F[u];
        if(FC[u]<=1) for(int v:E[u]) if(v!=f && F[u]!=F[v]) FR[u]+=FR[v];
    }
    
    //换根dp
    void dfs2(int u,int f) {
        if(f) GC[u]=G[u]=!G[f],GR[u]=GR[f]+!G[u];
        else GC[u]=G[u]=0,GR[u]=1;
        dp[u]=F[u]|G[u];
        if(!dp[u]) R[u]=FR[u]+GR[u]-1;
        else if(FC[u]+G[u]==1) {
            if(G[u]) R[u]=GR[u];
            else R[u]=FR[u];
        }
        for(int v:E[u]) if(v!=f) {
            GC[u]=FC[u]-!F[v]+(f?!G[f]:0);
            G[u]=GC[u]>0;
            GR[u]=0;
            if(!G[u]) GR[u]=GR[f]+SR[u]-FR[v]+1;
            else if(GC[u]==1) {
                if(son[u][0] && son[u][0]!=v) GR[u]=FR[son[u][0]];
                else if(son[u][1] && son[u][1]!=v) GR[u]=FR[son[u][1]];
                else GR[u]=GR[f];
            }
            dfs2(v,u);
        }
    }
    
    int cnt[2];
    int main(){
        n=rd(),scanf("%lld",&m);
        rep(i,2,n) {
            int u=rd(),v=rd();
            E[u].pb(v),E[v].pb(u);
        }
        dfs1(1,0),dfs2(1,0);
        // 矩阵处理
        rep(i,1,n) {
            cnt[dp[i]]++;
            X.a[1][dp[i]]=(X.a[1][dp[i]]+n)%P;
            X.a[0][!dp[i]]=(X.a[0][!dp[i]]+R[i])%P;
            X.a[0][dp[i]]=(X.a[0][dp[i]]+n-R[i])%P;
        }
        m--,Res.a[0][0]=Res.a[1][1]=1;
        while(m) {
            if(m&1) Res=Res*X;
            X=X*X;
            m>>=1;
        }
        int x=(1ll*Res.a[0][0]*cnt[0]+1ll*Res.a[1][0]*cnt[1])%P;
        int y=(1ll*Res.a[0][1]*cnt[0]+1ll*Res.a[1][1]*cnt[1])%P;
        // 得到第一层败/胜 的个数,与第0层合并
        int ans=0;
        if(!dp[1]) ans=1ll*x*R[1]%P;
        else ans=(1ll*x*(n-R[1])+1ll*n*y)%P;
        printf("%d
    ",ans);
    }
    
    
  • 相关阅读:
    解决WordPress不能发邮件,WordPress 无法发送邮件
    WordPress 显示 Fatal error: Allowed memory size of 134217728 bytes exhausted (tried to allocate 2097160 bytes)解决办法
    怎么优雅的取消重复请求ajax
    手拉手搭建一个脚手架
    数据库隔离级别RC与RR区别——MVCC、ReadView
    整理一下下一步的计划
    减肥
    EBS: Shift + F6: 当复制上行记录
    Oracle 表值函数之多表关联使用
    EBS: 序号授权 GRANT
  • 原文地址:https://www.cnblogs.com/chasedeath/p/14455224.html
Copyright © 2011-2022 走看看