DTOJ #3702. 月读(tsukuyomi)
【题目描述】
总而言之,月读现在有一棵大小为 ,树上每条边上有一个数字,月读有 次询问,每次询问一对 ,你需要回答从 到 的路径上的数字重新排列能否形成一个回文序列,若可行输出 ,否则输出 ,月读为了加快您的读入,每次询问的 是通过某种方式生成的,为了加快您的输出,你只需要最后输出回答 的个数和即可。
【输入格式】
第一行:两个整数
接下来 行:三个整数 ,描述一条边 与边上的数字 。
接下来一行
个询问以如下方式生成
【输出格式】
一行一个数字,代表回答 的个数。
【样例】
样例输入
4 1
1 2 1
2 3 1
1 4 2
2 3
样例输出
1
【数据范围与提示】
对于此题有六个测试点:
A(10分)
B(23分)
C(15分)
D(15分)
E(17分)
F(20分)
对于所有的
【题解】
回文序列一看就不是什么好东西。。。再认真读一遍题目,发现可以重新排列。那么这显然只需要统计每个数的出现次数是奇数还是偶数就可以了。显然这种判断奇偶性的可以用异或的方法。对于每个点存一下每种颜色的个数。询问时直接把两个节点的数组异或起来就可以了。这是最朴素的暴力。
考虑优化。维护数组,每个点只加入一次,树上主席树嘛!高兴地写着,一看数据范围: 有点小大。。。只能考虑 回答每次询问。
先从原来的思路想起。两个数组,维护的只有 和 ,这显然可以用 优化。
但还是过不去,让我们来考虑问题的本质:询问二进制位数。显然只有一位和 的是合法的。我们考虑能不能只用一个数来代表一个 。
幸运的是, 显然可以解决这个问题。我们本来每一位的权值是 ,考虑改变底数为 ,其中 是一个大质数。这样用自然溢出的 就可以把 的 次幂存下来,对于每个点求出树上前缀 异或和。这样对于每次询问的 ,只需判断 与 的 值的异或值是否为 或 即可。可以用 表维护。至此这题结束。
这个做法看起来真的很假。。。但我们要相信:数据是水的,出题人是懒的。。。于是信仰过了。
大质数可以采用神犇 推荐的 ,我试了试,果然没被卡 ,还一遍过了。。。
【代码】
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; } |
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· AI与.NET技术实操系列(二):开始使用ML.NET
· 记一次.NET内存居高不下排查解决与启示
· 探究高空视频全景AR技术的实现原理
· 理解Rust引用及其生命周期标识(上)
· 浏览器原生「磁吸」效果!Anchor Positioning 锚点定位神器解析
· DeepSeek 开源周回顾「GitHub 热点速览」
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· .NET 10首个预览版发布:重大改进与新特性概览!
· AI与.NET技术实操系列(二):开始使用ML.NET
· 单线程的Redis速度为什么快?