zoukankan      html  css  js  c++  java
  • fzyzojP2291 -- 小添添的庄园之道路修复

    直接换根dp

    f[i]表示,i为根的子树的方案

    f[i]=Π(f[son]+1)(就是考虑这个边修不修(不修,子树中只有一种方案))

    这里是乘法

    换根的时候,直接算的话,为了消除x对fa的贡献,要乘上逆元

    但是

    1.会TLE

    2.可能f[x]+1=1e9+7,也就是没有逆元(除以0是非法的)

    所以考虑用x前面的兄弟的答案和后面兄弟答案(两个连乘积)拼凑

    (不用每个点开一个vector记录儿子,直接每个x开一个数记录关于father的前后兄弟答案即可)

    换根的时候,把fa的贡献随便作为x儿子y的前面兄弟或者后面兄弟即可

    代码:

    #include<bits/stdc++.h>
    #define il inline
    #define reg register int
    #define numb (ch^'0')
    #define int long long
    using namespace std;
    typedef long long ll;
    il void rd(int &x){
        char ch;bool fl=false;
        while(!isdigit(ch=getchar()))(ch=='-')&&(fl=true);
        for(x=numb;isdigit(ch=getchar());x=x*10+numb);
        (fl==true)&&(x=-x);
    }
    namespace Miracle{
    const int N=1e6+6;
    const int mod=1e9+7;
    int n;
    struct node{
        int nxt,to;
    }e[2*N];
    int hd[N],cnt;
    void add(int x,int y){
        e[++cnt].nxt=hd[x];
        e[cnt].to=y;
        hd[x]=cnt;
    }
    int f[N];
    int ple[N],pri[N];
    int ans[N];
    int mo(int x){
        //return x>=mod?x-mod:x;
        return x%mod;
    }
    int sta[N],top;
    void dfs(int x,int fa){
        f[x]=1;
        int st=top;
        ll tmp=1;
        ple[x]=1;
        for(reg i=hd[x];i;i=e[i].nxt){
            int y=e[i].to;
            if(y==fa) continue;
            dfs(y,x);
            sta[++top]=y;
            ple[y]=f[x];
            f[x]=(ll)f[x]*(f[y]+1)%mod;
        }
        pri[x]=1;
        for(reg i=top;i>=st+1;--i){
            pri[sta[i]]=tmp;
            tmp=(ll)tmp*(f[sta[i]]+1)%mod;
        }
        top=st;
    }
    void sol(int x,int fa){
        if(x!=1){
            ll nle=1;
            //cout<<" x "<<x<<" : "<<f[x]<<" ple "<<ple[x]<<" pri "<<pri[x]<<endl;
            f[x]=(ll)f[x]*((ll)ple[x]*pri[x]%mod+1)%mod;
            ans[x]=f[x];
            nle=((ll)ple[x]*pri[x]%mod+1)%mod;
            //cout<<" x "<<x<<" : "<<nle<<endl;
            for(reg i=hd[x];i;i=e[i].nxt){
                int y=e[i].to;
                if(y==fa) continue;
                ple[y]=(ll)ple[y]*nle%mod;
                sol(y,x);
            }
        }
        else{
            ll nle=1;
            for(reg i=hd[x];i;i=e[i].nxt){
                int y=e[i].to;
                if(y==fa) continue;
                sol(y,x);
            }
        }
    }
    int main(){
        rd(n);
        int y;
        for(reg i=2;i<=n;++i){
            rd(y);add(i,y);add(y,i);
        }
        dfs(1,0);
        ans[1]=f[1];
        sol(1,0);
        for(reg i=1;i<=n;++i){
            printf("%lld ",ans[i]);
        }
        return 0;
    }
    
    }
    signed main(){
        freopen("2.in","r",stdin);
        freopen("2.out","w",stdout);
        Miracle::main();
        return 0;
    }
    
    /*
       Author: *Miracle*
       Date: 2019/2/9 10:35:35
    */

    换根的时候往往可能会除以0

    要警惕!

    前后兄弟处理无疑是最好的方法。

  • 相关阅读:
    Flink开发中的问题
    怎么确定一个Flink job的资源
    Flink统计日活
    Flink读取resource资源文件
    Spark-6-如何缓解消除数据倾斜
    Spark-5-如何定位导致数据倾斜的代码
    Spark-4-为何要处理数据倾斜
    集合遍历删除中遇到问题
    1206
    1205
  • 原文地址:https://www.cnblogs.com/Miracevin/p/10358462.html
Copyright © 2011-2022 走看看