19-11-2-M
最后一个当然要模自己辣。
%%%Miemengsb
ZJ一下:
三道题没有一道会的,唯一的20还是T2输出$n/2$得的
咝……
T1一看,只会暴力。
T2一看,像是状压,但是我是$dpsb$,于是弃掉了。
T3一看,GP不会,于是又打了一个暴力。
结果:
和*一样。
42
|
Miemeng | 0
00:00:18
|
20
00:00:37
|
0
00:00:53
|
20
00:00:53
|
这是TJ:
我GP不会,于是先咕掉了,吱。
T2
非常不会的时光倒流状压dp。
首先设$f_{i,s}$为时间为$i$,状态是$s$时是否可行。
然后我们发现一个神奇的性质……
当我们手玩一下性质时,就会发现所谓的拨动传递抵消是没有用的,如果两个拨动传递的时候抵消了,那么一定有上面的所有祖先全部被拨动了$2$次(也就是没有拨动)。
我们从末情况向前推,每次的操作就变成去掉一个点的影响,我们预处理出每个点在$i$秒后的影响以实现快速转移。
为什么从末情况向前推更加优呢?
因为每个点的影响时间是确定的(倒着),如果正向的话就可能需要枚举或是处理最后时间的情况。
这样就可以快乐的转移了
#include <iostream> #include <cstring> #include <cstdint> #include <climits> #include <cstdio> #define V 35 #define N 111 #define LL long long using namespace std; LL pn,tp,ans=INT_MAX; LL fa[N],aim[N],aimn, g[V][V],dp[V][(1<<17)+V]; int main(){ #ifndef LOCAL freopen("decoration.in" ,"r",stdin); freopen("decoration.out","w",stdout); #endif cin.sync_with_stdio(false); cin>>pn; fa[1]=0; for(int i=2;i<=pn;i++) cin>>fa[i]; for(int i=1;i<=pn;i++){ cin>>aim[i]; if(aim[i]==1) aimn|=1<<(i-1); } LL maxs=(1ll<<pn)-1; for(int i=1;i<=32;i++){ for(int j=0;j<=pn;j++){ LL cnt=0,now=j,tot=pn-i; while(cnt<=tot && now!=0){ cnt++; g[i][j]|=1<<(now-1); now=fa[now]; } } } dp[pn+1][0]=1; for(int i=pn;i>=1;i--){ for(int s=0;s<=maxs;s++){ if(!dp[i+1][s]) continue; for(int k=1;k<=pn;k++) dp[i][s^g[i][k]]|=dp[i+1][s]; dp[i][s]|=dp[i+1][s]; if(dp[i][aimn]){ cout<<pn-i+1<<endl; return 0; } } } }
Miemeng真的蒻