题目:
分析:
这道题的难点在于:
每一次转移的时候,要记录很多被翻转的点,还要确定他们下一次的fa是谁。
所以转换一下思路:从终止状态转移到0状态(初始状态),倒着来。
假设现在到了倒数第 i 秒,那么从第i~ans的时间内翻转了一个灯他是一直要向上跳并影响其他灯的,跳的时间为i,所以可以预处理一下:fa[i][j]表示i灯在跳j步后能变化到的状态
每一次转移的时候,只需要枚举灯,异或上fa数组即可快速知道倒数第i+1秒的状态。
某一次更新到了初始状态0,就直接输出现在是倒数第几秒即可。
因为还要记录时间,所以dp要开两维。
注意:如果一个点已经传递到了根节点,那么状态就不会变了!!
#include<bits/stdc++.h> using namespace std; #define N 18 #define ri register int int ff[N],fa[N][N],dp[N][1<<N],n,goal=0,x; int main() { freopen("decoration.in","r",stdin); freopen("decoration.out","w",stdout); scanf("%d",&n); ff[1]=0; for(ri i=2;i<=n;++i) scanf("%d",&x),ff[i]=x; for(ri i=1;i<=n;++i) scanf("%d",&x),goal<<=1,goal+=x; for(ri i=1;i<=n;++i){ fa[i][0]=(1<<(n-i)); int pre=ff[i]; for(ri j=1;j<=n;++j){ if(pre) fa[i][j]=fa[i][j-1]^(1<<(n-pre));//如果说已经到达根节点1了,就不用再跳了 else fa[i][j]=fa[i][j-1]; pre=ff[pre]; } } dp[0][goal]=1; for(ri t=0;t<=n;++t){ if(dp[t][0]) { printf("%d\n",t); return 0; } for(ri j=0;j<(1<<n);++j) if(dp[t][j]){ dp[t+1][j]=1; for(ri k=1;k<=n;++k) dp[t+1][j^(fa[k][t])]=1; } } return 0; } /* 7 1 1 2 2 3 3 0 1 1 1 0 0 1 3 7 1 1 2 2 3 3 1 1 1 1 0 0 1 3 8 1 1 2 4 2 4 6 0 0 1 0 1 1 0 1 3 1 1 0 1 1 */