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

要警惕!

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

猜你喜欢

转载自www.cnblogs.com/Miracevin/p/10358462.html
今日推荐