DTOJ #3702. 月读(tsukuyomi)
【题目描述】
总而言之,月读现在有一棵大小为 $N$ ,树上每条边上有一个数字,月读有 $M$ 次询问,每次询问一对 $(x,y)$ ,你需要回答从 $x$ 到 $y$ 的路径上的数字重新排列能否形成一个回文序列,若可行输出 $Yes$ ,否则输出 $No$ ,月读为了加快您的读入,每次询问的 $x,y$ 是通过某种方式生成的,为了加快您的输出,你只需要最后输出回答 $Yes$ 的个数和即可。
【输入格式】
第一行:两个整数 $N,M$
接下来 $N-1$ 行:三个整数 $u_i,v_i,w_i$,描述一条边 $(u_i,v_i)$ 与边上的数字 $w_i$ 。
接下来一行 $A_1,B_1$
$M$ 个询问以如下方式生成
$x_i=A_i \% n+1,y_i=B_i \% n+1$
$A_i=A_{i-1} \times 666073 \% (10^9+7)$
$B_i=B_{i-1} \times 233 \% 998244353$
【输出格式】
一行一个数字,代表回答 $Yes$ 的个数。
【样例】
样例输入
4 1
1 2 1
2 3 1
1 4 2
2 3
样例输出
1
【数据范围与提示】
对于此题有六个测试点:
A(10分) $N,M \le 2000$
B(23分) $N,M \le 10^5,u_i=v_i+1$
C(15分) $N,M \le 10^5$
D(15分) $N,M \le 10^6,w_i \le 30 $
E(17分) $N,M \le 10^6$
F(20分) $N \le 10^6,M \le 10^7$
对于所有的 $1 \le w_i \le n$
【题解】
回文序列一看就不是什么好东西。。。再认真读一遍题目,发现可以重新排列。那么这显然只需要统计每个数的出现次数是奇数还是偶数就可以了。显然这种判断奇偶性的可以用异或的方法。对于每个点存一下每种颜色的个数。询问时直接把两个节点的数组异或起来就可以了。这是最朴素的暴力。
考虑优化。维护数组,每个点只加入一次,树上主席树嘛!高兴地写着,一看数据范围:$M$ 有点小大。。。只能考虑 $O(1)$ 回答每次询问。
先从原来的思路想起。两个数组,维护的只有 $0$ 和 $1$,这显然可以用 $bitset$ 优化。
但还是过不去,让我们来考虑问题的本质:询问二进制位数。显然只有一位和 $0$ 的是合法的。我们考虑能不能只用一个数来代表一个 $bitset$。
幸运的是, $hash$ 显然可以解决这个问题。我们本来每一位的权值是 $2^{w_i}$,考虑改变底数为 $p^{w_i}$,其中 $p$ 是一个大质数。这样用自然溢出的 $hash$ 就可以把 $p$ 的 $1-n$ 次幂存下来,对于每个点求出树上前缀 $hash$ 异或和。这样对于每次询问的 $(x,y)$,只需判断 $x$ 与 $y$ 的 $hash$ 值的异或值是否为 $p^i$ 或 $0$ 即可。可以用 $hash$ 表维护。至此这题结束。
这个做法看起来真的很假。。。但我们要相信:数据是水的,出题人是懒的。。。于是信仰过了。
大质数可以采用神犇 $1124828077$ 推荐的 $13131313$,我试了试,果然没被卡 $hash$,还一遍过了。。。
【代码】
#include<bits/stdc++.h> inline int read ( void ) { int x=0;char ch;bool f=false; while ( !isdigit(ch=getchar()) ) if ( ch=='-' ) f=true; for ( x=ch^48;isdigit(ch=getchar()); ) x=(x<<1)+(x<<3)+(ch^48); return f ? -x : x ; } int n,m,tot,cnt_e,h[1000010]; struct edge { int v,nxt,w; } e[2000010]; std::map<int,int> map; inline void add ( int u,int v,int w ) { e[++cnt_e].nxt=h[u];e[h[u]=cnt_e].v=v;e[cnt_e].w=w; e[++cnt_e].nxt=h[v];e[h[v]=cnt_e].v=u;e[cnt_e].w=w; } typedef unsigned long long Hash; const Hash HASH=13131313,hash_mod=10000019; Hash Pow[1000010],hsh[1000010]; struct HASH_MAP { int h[hash_mod+1],cnt; struct edge { Hash v;int nxt; } e[1000010]; inline void insert ( Hash val ) { e[++cnt].nxt=h[val%hash_mod];e[h[val%hash_mod]=cnt].v=val; } inline bool find ( Hash val ) { for ( int i=h[val%hash_mod];i;i=e[i].nxt ) if ( e[i].v==val ) return true; return false; } }hash_map; inline void dfs ( int u,int fr ) { for ( int i=h[u];i;i=e[i].nxt ) if ( e[i].v!=fr ) hsh[e[i].v]=hsh[u]^Pow[e[i].w],dfs(e[i].v,u); } signed main() { n=read();m=read(); for ( int i=1,u,v,w;i<n;i++ ) { u=read();v=read();w=read(); if ( !map.count(w) ) map[w]=++tot; add(u,v,map[w]); } Pow[0]=1; for ( int i=1;i<=tot;i++ ) Pow[i]=Pow[i-1]*HASH,hash_map.insert(Pow[i]); dfs(1,0); int ans=0; for ( int A=read(),B=read();m--; ) { int x=A%n+1,y=B%n+1;A=A*666073LL%1000000007;B=B*233LL%998244353; if ( !(hsh[x]^hsh[y]) or hash_map.find(hsh[x]^hsh[y]) ) ans++; } printf("%d\n",ans); return 0; }