CF1779F Xorcerer’s Stones
树形dp记录路径
首先我们分析一下操作。
对于奇树,进行一次操作后,其异或和不变;对于偶树,进行一次操作后,其异或和为0。
如果我们能让所有点异或和为0,只要在根节点再进行一次操作,就可以得到一种合法操作方案,考虑树形dp出是否存在一种方案, d p u , j dp_{u,j} dpu,j表示以 u u u为根节点的子树内,是否存在异或和为 j j j的操作方案。合并很简单,但是对于 s z u sz_u szu为偶数时,不论其子树内操作如何,都可以进行一次操作,使得 d p u , 0 = 1 dp_{u,0}=1 dpu,0=1。
对于方案的记录,我们开一个 p r e v , j x o r k = j pre_{v,jxork}=j prev,jxork=j,表示 d p u , j x o r k = 1 dp_{u,jxork}=1 dpu,jxork=1这种方案是在儿子异或和为 k k k的情况下,根节点异或和为 j j j的情况下合并转移而来的,因为dp时 u u u的儿子由前往后遍历转移,那么输出路径时就应该由后往前遍历
#include
#define ll long long
struct node{
int x,y;
};
void solve(){
int n;
std::cin>>n;
std::vector<int> a(n+1);
for (int i=1;i<=n;i++){
std::cin>>a[i];
}
std::vector<std::vector<int>> e(n+1);
for (int i=2;i<=n;i++){
int x;
std::cin>>x;
e[x].push_back(i);
e[i].push_back(x);
}
std::vector<int> sz(n+1);
std::vector<std::vector<int>> dp(n+1,std::vector<int>(32)),pre(n+1,std::vector<int>(32));
std::function<void(int,int)> dfs=[&](int u,int fa){
sz[u]=1;
dp[u][a[u]]=1;
for (auto v:e[u]){
if (v==fa) continue;
dfs(v,u);
std::vector<int> use(32);
std::swap(dp[u],use);
for (int j=0;j<32;j++){
if (!use[j]) continue;
for (int k=0;k<32;k++){
if (!dp[v][k]) continue;
dp[u][j^k]=1;
pre[v][j^k]=j;
}
}
sz[u]+=sz[v];
}
if (!(sz[u]&1)){
dp[u][0]=1;
}
};
dfs(1,-1);
std::vector<int> ans;
std::function<void(int,int,int)> prin=[&](int u,int fa,int sum){
if (!(sz[u]&1)&&!sum){
ans.push_back(u);
}
reverse(e[u].begin(),e[u].end());
for (auto v:e[u]){
if (v==fa) continue;
prin(v,u,sum^pre[v][sum]);
sum=pre[v][sum];
}
};
if (dp[1][0]){
prin(1,-1,0);
std::cout<<ans.size()+1<<"\n";
for (auto i:ans){
std::cout<<i<<" ";
}
std::cout<<"1";
}else{
std::cout<<"-1";
}
}
int main(){
std::ios::sync_with_stdio(false);
std::cin.tie(nullptr);
std::cout.tie(nullptr);
solve();
return 0;
}