zoukankan      html  css  js  c++  java
  • 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} imes 666073 \% (10^9+7)$

    $B_i=B_{i-1} imes 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
    ",ans);
    	return 0;
    }
    
  • 相关阅读:
    centos 编码问题 编码转换 cd到对应目录 执行 中文解压
    centos 编码问题 编码转换 cd到对应目录 执行 中文解压
    centos 编码问题 编码转换 cd到对应目录 执行 中文解压
    Android MVP 十分钟入门!
    Android MVP 十分钟入门!
    Android MVP 十分钟入门!
    Android MVP 十分钟入门!
    mysql备份及恢复
    mysql备份及恢复
    mysql备份及恢复
  • 原文地址:https://www.cnblogs.com/RenSheYu/p/11305838.html
Copyright © 2011-2022 走看看