CodeForces - 1252B Cleaning Robots
题意:
给你一棵用 N 个点构成的树,问这 n 个点有多少种划分方案。一种划分方案合法当且仅当这种划分方案将所有点分成若干集合,每一个集合中的点正好可以构成一条链,没有两个集合所形成的链可以首尾相接,一个点也算链。
题解:
这道题还是比较显然的树上DP,一开始想的是设f[x][0/1/2],0 表示 x 这个点不可能向上连边,1 表示 x 这个点可以向上连边也可以不连边, 2 表示这个点一定向上连边。这么设状态向上很好转移,但是平级就很难,所以我设f[x][0/1/2/3/4]。
0表示x这个点没有儿子有向他连边的可能
1表示这个点有一个或以上的儿子有向他连边的可能
2表示这个点已经和一个儿子连边,且没有其他儿子有向他连边的可能
3表示这个点已经和一个儿子连边,且有其他儿子有向他连边的可能
4表示这个点已经和两个儿子连边
这样,虽然状态数变多了,但是儿子与儿子之间,儿子与父亲之间的转移变得更加清晰。
1 #include<cstdlib> 2 #include<cstdio> 3 #include<iostream> 4 #include<cstring> 5 #include<algorithm> 6 #include<cmath> 7 #define N 100005 8 using namespace std; 9 int n; 10 int zz,a[N]; 11 const int p=1e9+7; 12 struct ro{ 13 int to,next; 14 }road[N*2]; 15 void build(int x,int y) 16 { 17 zz++; 18 road[zz].to=y; 19 road[zz].next=a[x]; 20 a[x]=zz; 21 } 22 int fa[N]; 23 long long F[N][10]; 24 void dfs(int x) 25 { 26 long long f[2][10]; 27 memset(f,0,sizeof(f)); 28 int nw=1,la=0; 29 f[nw][0]=1; 30 for(int i=a[x];i;i=road[i].next) 31 { 32 int y=road[i].to; 33 if(y==fa[x])continue; 34 fa[y]=x; 35 dfs(y); 36 nw^=1,la^=1; 37 memset(f[nw],0,sizeof(f[nw])); 38 f[nw][0]=f[la][0]*F[y][4]%p; 39 f[nw][1]=(f[la][0]*((F[y][0]+F[y][2])%p))%p+(f[la][1]*((F[y][0]+F[y][2]+F[y][4])%p))%p; 40 f[nw][1]%=p; 41 f[nw][2]=(f[la][0]*((F[y][0]+F[y][2]+F[y][3])%p))%p+f[la][2]*F[y][4]%p; 42 f[nw][2]%=p; 43 f[nw][3]=(f[la][1]*((F[y][0]+F[y][2]+F[y][3])%p))%p+(f[la][2]*((F[y][0]+F[y][2])%p))%p+(f[la][3]*((F[y][0]+F[y][2]+F[y][4])%p))%p; 44 f[nw][3]%=p; 45 f[nw][4]=((f[la][2]+f[la][3])%p*((F[y][0]+F[y][2]+F[y][3])%p))%p+(f[la][4]*((F[y][0]+F[y][2]+F[y][4])%p))%p; 46 f[nw][4]%=p; 47 } 48 for(int i=0;i<=4;i++) F[x][i]=f[nw][i]; 49 } 50 51 int main() 52 { 53 scanf("%d",&n); 54 for(int i=1;i<n;i++) 55 { 56 int x,y; 57 scanf("%d%d",&x,&y); 58 build(x,y); 59 build(y,x); 60 } 61 dfs(1); 62 /* for(int i=1;i<=n;i++) 63 { 64 for(int j=0;j<=4;j++) 65 { 66 cout<<F[i][j]<<' '; 67 } 68 cout<<endl; 69 }*/ 70 long long ans=(F[1][0]+F[1][2]+F[1][4])%p; 71 printf("%lld\n",ans); 72 return 0; 73 }