最后一个当然要模自己辣。
%%%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;
}
}
}
}