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;
}

 

posted @ 2019-08-05 22:32  DTOI_RSY  阅读(288)  评论(0编辑  收藏  举报