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;
    }
    
  • 相关阅读:
    汇编语言实验8
    汇编语言中的错误
    第一篇自己完成的宏汇编
    偶尔发现的一个可以理解的问题
    程序运行时的ds cs
    第一篇博客UVA201
    将Gridview数据导出到excel
    GridView 中的下载功能
    GridView 加行号
    修改DataTable中的值
  • 原文地址:https://www.cnblogs.com/RenSheYu/p/11305838.html
Copyright © 2011-2022 走看看