• CF543D Road Improvement 题解


    Codeforces
    Luogu

    Description.

    \(n\) 个点的树,给边染色。
    一个合法的方案定义为所有点到 \(x\) 的路径上只有一条黑边。
    对所有 \(x\) 求出合法方案方案数。

    Solution.

    哈哈,这题我出思路用了 1min,然后被代码细节折磨了 4+h!!!!出题人死个马

    首先定义 \(f_{x,0}\)\(f_{x,1}\)\(x\) 节点祖先 没有/有 黑边的方案数。
    然后有一个显然的转移,就是

    \[f_{x,0}=\prod_{y\in\text{son}(x)}(f_{y,0}+f_{y,1})\\ f_{x,1}=\prod_{y\in\text{son}(x)}f_{y,1}\\ \]

    挺显然的,就是枚举这条边是否染黑。

    确认完正确性后,就直接开始考虑换根。
    定义 \(g_{x,0},g_{x,1}\) 表示以 \(x\) 为根总共的答案。
    根据换根的套路,写出了这样一份代码
    感觉根本没问题,但是 WA on test 10。
    就你考虑定义 \(f0,f1\) 分别如果把它父亲当作子树的贡献,那有
    \(f0=\frac{g_{fa,0}}{f_{x,0}+f_{x,1}},f1=\frac{g_{fa,1}}{f_{x,1}}\)
    就考虑把当前孩子的贡献去掉。
    然后就调啊调啊调啊调啊,最后发现。
    tm 这个 \(f_{x,0}+f_{x,1} \bmod P\) 是可以等于 \(0\)
    然后 tm 就没逆元了,卡这个出题人怕不是马早没了。

    然后直接拆一下式子,记录乘上的 \(0\) 的个数。
    然后直接做就做完了,但是细节挺多的

    Coding.

    点击查看代码
    //Coded by Kamiyama_Shiki on 2021.11.16 {{{
    //是啊……你就是那只鬼了……所以被你碰到以后,就轮到我变成鬼了
    #include<bits/stdc++.h>
    #define debug(...) fprintf(stderr,__VA_ARGS__)
    using namespace std;typedef long long ll;
    template<typename T>inline void read(T &x)
    {
    	x=0;char c=getchar(),f=0;
    	for(;c<48||c>57;c=getchar()) if(!(c^45)) f=1;
    	for(;c>=48&&c<=57;c=getchar()) x=(x<<1)+(x<<3)+(c^48);
    	f?x=-x:x;
    }
    template<typename T,typename...L>inline void read(T &x,L&...l) {read(x),read(l...);}//}}}
    const int N=200005,P=1000000007;
    int fc[N],fi[N],iv[N];//dbinit{{{
    inline int ksm(int x,int q=P-2) {int r=1;for(;q;q>>=1,x=1ll*x*x%P) if(q&1) r=1ll*r*x%P;return r;}
    inline void dbinit(int n=N-1)
    {
    	fc[0]=1;for(int i=1;i<=n;i++) fc[i]=1ll*fc[i-1]*i%P;
    	iv[1]=1;for(int i=2;i<=n;i++) iv[i]=1ll*iv[P%i]*(P-P/i)%P;
    	fi[0]=1;for(int i=1;i<=n;i++) fi[i]=1ll*fi[i-1]*iv[i]%P;
    }
    inline int C(int n,int m) {return n<0||m<0||n<m?0:1ll*fc[n]*fi[m]%P*fi[n-m]%P;}//}}}
    struct edge{int to,nxt;}e[N<<1];int n,et,head[N],f[N][2],g[N][2],h[N];
    inline void adde(int x,int y) {e[++et]=(edge){y,head[x]},head[x]=et;}
    inline void dfs0(int x,int fa)
    {
    	f[x][0]=f[x][1]=1;
    	for(int i=head[x],y;i;i=e[i].nxt) if((y=e[i].to)!=fa)
    		dfs0(y,x),f[x][0]=1ll*f[x][0]*(f[y][0]+f[y][1])%P,f[x][1]=1ll*f[x][1]*f[y][1]%P;
    }
    inline void dfs1(int x,int fa)
    {
    	//h[y]=f[x][0]/(f[y][0]+f[y][1])*(h[x]+f1)
    	int f1=1ll*g[fa][1]*ksm(f[x][1])%P,tmp=1,tot=0;
    	g[x][0]=1ll*f[x][0]*(h[x]+f1)%P,g[x][1]=1ll*f[x][1]*f1%P;//h[x]=f0
    	for(int i=head[x],y;i;i=e[i].nxt) if((y=e[i].to)!=fa)
    		{if((f[y][0]+f[y][1])%P) tmp=1ll*tmp*(f[y][0]+f[y][1])%P;else tot++;}
    	for(int i=head[x],y;i;i=e[i].nxt) if((y=e[i].to)!=fa)
    		{if((f[y][0]+f[y][1])%P) h[y]=tot?0ll:1ll*tmp*ksm(f[y][0]+f[y][1])%P;else h[y]=tot-1?0:tmp;}
    	for(int i=head[x],y;i;i=e[i].nxt) if((y=e[i].to)!=fa) h[y]=1ll*h[y]*(f1+h[x])%P;
    	for(int i=head[x];i;i=e[i].nxt) if(e[i].to!=fa) dfs1(e[i].to,x);
    }
    int main()
    {
    	read(n);for(int i=2,f;i<=n;i++) read(f),adde(f,i),adde(i,f);
    	dfs0(1,0),h[1]=0,g[0][1]=f[1][1],dfs1(1,0);
    	for(int i=1;i<=n;i++) printf("%d%c",g[i][0],i==n?'\n':' ');
    	return 0;
    }
    
  • 相关阅读:
    Qt 无边框窗体改变大小 完美实现
    深入Windows窗体原理及控件重绘技巧
    EF里Guid类型数据的自增长、时间戳和复杂类型的用法
    Entity Framework中的Identity map和Unit of Work模式
    使用SQLite数据库和Access数据库的一些经验总结
    实现Avl平衡树
    使用Ajax
    接口和类 反射的差异性
    Guacamole 介绍
    依赖注入(DI)和Ninject
  • 原文地址:https://www.cnblogs.com/pealfrog/p/15561967.html
Copyright © 2020-2023  润新知