DTOJ #3702. 月读(tsukuyomi)

【题目描述】

总而言之,月读现在有一棵大小为 N  ,树上每条边上有一个数字,月读有 M  次询问,每次询问一对 (x,y)  ,你需要回答从 x  到 y  的路径上的数字重新排列能否形成一个回文序列,若可行输出 Yes  ,否则输出 No  ,月读为了加快您的读入,每次询问的 x,y 是通过某种方式生成的,为了加快您的输出,你只需要最后输出回答 Yes 的个数和即可。

【输入格式】

第一行:两个整数 N,M

接下来 N1 行:三个整数 ui,vi,wi,描述一条边 (ui,vi) 与边上的数字 wi

接下来一行 A1,B1

M 个询问以如下方式生成

xi=Ai%n+1,yi=Bi%n+1

Ai=Ai1×666073%(109+7)

Bi=Bi1×233%998244353

【输出格式】

一行一个数字,代表回答 Yes 的个数。

【样例】

样例输入

4 1
1 2 1
2 3 1
1 4 2
2 3

样例输出

1

【数据范围与提示】

对于此题有六个测试点:

A(10分) N,M2000

B(23分) N,M105,ui=vi+1

C(15分) N,M105

D(15分) N,M106,wi30

E(17分) N,M106

F(20分) N106,M107

对于所有的 1win

【题解】

回文序列一看就不是什么好东西。。。再认真读一遍题目,发现可以重新排列。那么这显然只需要统计每个数的出现次数是奇数还是偶数就可以了。显然这种判断奇偶性的可以用异或的方法。对于每个点存一下每种颜色的个数。询问时直接把两个节点的数组异或起来就可以了。这是最朴素的暴力。

考虑优化。维护数组,每个点只加入一次,树上主席树嘛!高兴地写着,一看数据范围:M 有点小大。。。只能考虑 O(1) 回答每次询问。

先从原来的思路想起。两个数组,维护的只有 0 和 1,这显然可以用 bitset 优化。

但还是过不去,让我们来考虑问题的本质:询问二进制位数。显然只有一位和 0 的是合法的。我们考虑能不能只用一个数来代表一个 bitset

幸运的是, hash 显然可以解决这个问题。我们本来每一位的权值是 2wi,考虑改变底数为 pwi,其中 p 是一个大质数。这样用自然溢出的 hash 就可以把 p 的 1n 次幂存下来,对于每个点求出树上前缀 hash 异或和。这样对于每次询问的 (x,y),只需判断 x 与 y 的 hash 值的异或值是否为 pi 或 0 即可。可以用 hash 表维护。至此这题结束。

这个做法看起来真的很假。。。但我们要相信:数据是水的,出题人是懒的。。。于是信仰过了。

大质数可以采用神犇 1124828077 推荐的 13131313,我试了试,果然没被卡 hash,还一遍过了。。。

【代码】

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
#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 @   DTOI_RSY  阅读(294)  评论(0编辑  收藏  举报
编辑推荐:
· AI与.NET技术实操系列(二):开始使用ML.NET
· 记一次.NET内存居高不下排查解决与启示
· 探究高空视频全景AR技术的实现原理
· 理解Rust引用及其生命周期标识(上)
· 浏览器原生「磁吸」效果!Anchor Positioning 锚点定位神器解析
阅读排行:
· DeepSeek 开源周回顾「GitHub 热点速览」
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· .NET 10首个预览版发布:重大改进与新特性概览!
· AI与.NET技术实操系列(二):开始使用ML.NET
· 单线程的Redis速度为什么快?
点击右上角即可分享
微信分享提示