这是一道好题……
关于这篇文章我为什么不和\(dsu\) \(on\) \(tree\)放一起,因为这题太好了,单独拿出来会好些
这是可爱的链接
先大致扫一眼题意,发现一条路径满足条件,当且仅当上面至多有一种字母出现次数为奇数次,总共有\(22\)种,因此我们可以考虑状压,\(0\)表示一个字母出现偶数次,\(1\)表示出现奇数次,用异或处理即可。
再看一下题目,很像一道点分治的题(真的,树上路径你告诉我不像?)但是这道题是一颗定根树,树根为\(1\)。
显然不能点分治,但是点分治的思路可以借鉴一下,考虑树上启发式合并\(dsu\) \(on\) \(tree\)。
即在做答案时用点分治的方式做(一条路径可行的情况只有\(23\)种),维护信息时用树上启发式合并的方法取维护。
我们维护一个\(f[x]\)表示从根节点出发路径异或之后的值为\(x\)的最大深度。
这一题因为用到了点分治的思路,所以相当于我们做答案时是先算了\(x\)的每个子树的答案,在考虑所有过\(x\)的路径,因此我们在\(dsu\) \(on\) \(tree\)上做答案时递归需要保留\(x\)(但这种情况我还没改出来,有知道的怎么改的跟我讲一下好吗?)
#include
using namespace std;
inline int read()
{
int f=1,w=0;char x=0;
while(x<'0'||x>'9') {if(x=='-') f=-1; x=getchar();}
while(x!=EOF&&x>='0'&&x<='9') {w=(w<<3)+(w<<1)+(x^48);x=getchar();}
return w*f;
}
const int N=500010;
int num_edge,n,Tim,Id[N];
int head[N],Dep[N],Xor[N],f[1<<22],End[N];
int Vis[N],Siz[N],Max[N],ans[N],Dfn[N];
struct Edge{int next,to,dis;} edge[N];
inline void Add(int from,int to,int dis)
{
edge[++num_edge].next=head[from];
edge[num_edge].dis=dis;
edge[num_edge].to=to;
head[from]=num_edge;
}
inline void Dfs_For_Pre(int pos,int fth,int dis)
{
Dfn[pos]=++Tim,Id[Dfn[pos]]=pos;Siz[pos]=1;
Dep[pos]=Dep[fth]+1;Xor[pos]=Xor[fth]^dis;
for(int i=head[pos];i;i=edge[i].next)
{
Dfs_For_Pre(edge[i].to,pos,edge[i].dis);
Siz[pos]+=Siz[edge[i].to];
if(Siz[Max[pos]]