BZOJ 1063 道路设计NOI2008

http://www.lydsy.com/JudgeOnline/problem.php?id=1063

题意:给你一棵树,也有可能是不连通的,把树分成几个链,求每个点到根经过的最大链数最小,而且要输出方案数。

思路:考虑dp,f[i][j]代表第i个节点,最大链数是j,那么有

f[i][j][0]代表已经向子树连接了0个链

f[i][j][1]代表已经向子树连接了1个链

f[i][j][2]代表已经向子树连接了2个链

这样转移就是

f1=f[pur][b][0]+f[pur][b][1]


f2=f[pur][b-1][0]+f[pur][b-1][1]+f[pur][b-1][2]


f[x][b][2]=f2*f[x][b][2]+f1*f[x][b][1]


f[x][b][1]=f1*f[x][b][0]+f2*f[x][b][1]


f[x][b][0]=f[x][b][0]*f2

pur代表x的儿子

 1 #include<cstdio>
 2 #include<cmath>
 3 #include<iostream>
 4 #include<cstring>
 5 #include<algorithm>
 6 #define ll long long
 7 ll Mod;
 8 int first[200005],next[200005],go[200005],tot;
 9 int n,m,fa[200005];
10 ll f[100005][12][3];
11 int read(){
12     int t=0,f=1;char ch=getchar();
13     while (ch<'0'||ch>'9') {if (ch=='-') f=-1;ch=getchar();}
14     while ('0'<=ch&&ch<='9') {t=t*10+ch-'0';ch=getchar();}
15     return t*f;
16 }
17 void insert(int x,int y){
18         tot++;
19         go[tot]=y;
20         next[tot]=first[x];
21         first[x]=tot;
22 }
23 void add(int x,int y){
24         insert(x,y);insert(y,x);
25 }
26 int find(int x){
27     if (fa[x]==x) return x;else return fa[x]=find(fa[x]);
28 }
29 ll get(ll x){
30     if (x%Mod!=0) return x%Mod;
31     if (x!=0) return Mod;
32     return 0;
33 }
34 void dfs(int x,int b,int fa){
35     f[x][b][0]=1;
36     f[x][b][1]=0;
37     f[x][b][2]=0;
38     for (int i=first[x];i;i=next[i]){
39         int pur=go[i];
40         if (pur==fa) continue;
41         dfs(pur,b,x);
42         ll f1=(f[pur][b][0]+f[pur][b][1]);    
43         ll f2;
44         if (b) f2=f[pur][b-1][0]+f[pur][b-1][1]+f[pur][b-1][2];else f2=0;
45         f[x][b][2]=get(f2*f[x][b][2]+f1*f[x][b][1]);
46         f[x][b][1]=get(f1*f[x][b][0]+f2*f[x][b][1]);
47         f[x][b][0]=get(f[x][b][0]*f2);
48     }
49 }
50 int main(){
51     n=read();m=read();Mod=read();
52     for (int i=1;i<=n;i++) fa[i]=i;
53     for (int i=1;i<=m;i++){
54         int x=read(),y=read();
55         add(x,y);
56         int p=find(x),q=find(y);
57         if (p!=q) fa[q]=p;    
58     }
59     for (int i=2;i<=n;i++)
60         if (find(i)!=find(1)){printf("-1\n-1\n");return 0;}
61     for (int i=0;;i++){
62         dfs(1,i,0);
63         if (f[1][i][0]+f[1][i][1]+f[1][i][2]){
64              printf("%d\n",i);
65              printf("%lld\n",((f[1][i][0]+f[1][i][1])%Mod+f[1][i][2])%Mod);
66              return 0;
67             }
68     }    
69 }

 

转载于:https://www.cnblogs.com/qzqzgfy/p/5610925.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值