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 <bits/stdc++.h>
#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;
}