七海ノ心象素描

所有的为时已晚都是恰逢其时

Nanami^2 avatar

Author

Nanami^2

大一学生 | 不是很聪明的样子

Telegram

我的频道

不定期推送灵感笔记

t.me/kisanami_blog
联系方式
QQ 群
1072614878
发送邮件

luogu7717「EZEC-10」序列 题解

Solution

Nanami^2
Nanami^2 Even in the rain.
2023年09月22日
预计阅读 4 分钟
820 字

想了想还是把这题单独拿出来了。

Solution

由于异或运算满足交换律和结合律,所以我们对于限制 (xi,yi,zi)(x_i,y_i,z_i),连边 (xi,yi)(x_i, y_i),权值为 ziz_i,这样只要确定了连通块中一个点,其他点的点权也可以确定。

会构成若干连通块,对于每个连通块分别考虑。

随便钦定一个点为根,进行 DFS\text{DFS} 得到每个点 xx 与根的关系 dxd_x,满足 arootax=dxa_{root} \oplus a_x = d_x

先把存在环且不合法的情况判掉,然后我们把 dxd_x 插入 0-1 Trie。

考虑 Trie 树上 DFS\text{DFS}。设 f(x,δ,k)f(x,\delta,k) 为在节点 xx,考虑了前 δ\delta 位,此时最大值为 kk 的方案数。

唯一的限制在于每个数必须在 [0,K][0,K] 之间。

规定左儿子为 00 儿子,右儿子为 11 儿子。

如果节点 xx 同时存在左右儿子,那么无论根取什么值,最大值一定会加上 2δ2^{\delta},所以

f(x,δ,k)=f(son0(x),δ1,k+2δ)+f(son1(x),δ1,k+2δ)f(x,\delta,k) = f\Big(son_0(x),\delta-1,k+2^{\delta}\Big) + f\Big(son_1(x),\delta-1,k+2^{\delta}\Big)

如果只有左儿子或右儿子,那么

  • 只有左儿子且 k+2δKk+2^{\delta} \le K。那么如果这一位放 11,那么方案就是 f(son0(x),δ1,k+2δ)f\Big(son_0(x),\delta-1,k+2^{\delta}\Big),否则无论后面放什么都不会超过 kk,方案数 2δ2^{\delta}
  • 只有左儿子且 k+2δ>Kk+2^{\delta} > K。那么只能放 11,方案数 f(son0(x),δ1,k)f\Big(son_0(x),\delta-1,k\Big)

只有右儿子的情况类似。

特判孤点,方案是 k+1k+1,直接乘起来。

#include<bits/stdc++.h>
using namespace std;
#define int long long
#define SET(a,b) memset(a,b,sizeof(a))
#define rep(i,j,k) for(int i=(j);i<=(k);++i)
#define per(i,j,k) for(int i=(j);i>=(k);--i)
int read() {
int a=0, f=1; char c=getchar();
while(!isdigit(c)) {
if(c=='-') f=-1;
c=getchar();
}
while(isdigit(c)) a=a*10+c-'0', c=getchar();
return a*f;
}
const int N=5e5+5, mod=1e9+7;
int n, m, K, ans=1, d[N], deg[N];
bool vis[N];
struct Gr {
int tot, h[N], to[N<<1], nxt[N<<1], w[N<<1];
void add(int x,int y,int z) {
to[++tot]=y, w[tot]=z, nxt[tot]=h[x], h[x]=tot;
}
} G;
namespace Trie {
int cnt=1, t[N*29][2];
void init() {
rep(i,0,cnt) t[i][0]=t[i][1]=0;
cnt=1;
}
void insert(int S) {
int x=1;
for(int i=29;~i;--i) {
int a=(S>>i)&1;
if(!t[x][a]) t[x][a]=++cnt;
x=t[x][a];
}
}
int Dfs(int x,int d,int k) {
if(k>K) return 0;
if(!t[x][0]&&!t[x][1]) return 1;
if(t[x][0]&&t[x][1]) return Dfs(t[x][0],d-1,k+(1<<d))+Dfs(t[x][1],d-1,k+(1<<d));
if(t[x][0]) {
if(k+(1<<d)<=K) return (1<<d)+Dfs(t[x][0],d-1,k+(1<<d));
return Dfs(t[x][0],d-1,k);
} else {
if(k+(1<<d)<=K) return (1<<d)+Dfs(t[x][1],d-1,k+(1<<d));
return Dfs(t[x][1],d-1,k);
}
}
};
void dfs(int x) {
vis[x]=1;
Trie::insert(d[x]);
for(int i=G.h[x];i;i=G.nxt[i]) {
int y=G.to[i], z=G.w[i];
if(d[y]!=-1&&(d[x]^z^d[y])!=0) {
puts("0");
exit(0);
}
d[y]=d[x]^z;
if(!vis[y]) dfs(y);
}
}
signed main() {
n=read(), m=read(), K=read();
rep(i,1,m) {
int x=read(), y=read(), z=read();
G.add(x,y,z), G.add(y,x,z);
++deg[x], ++deg[y];
}
SET(d,-1);
for(int i=1;i<=n;++i) {
if(vis[i]) continue;
if(!deg[i]) { (ans*=K+1)%=mod; continue; }
Trie::init();
d[i]=0;
dfs(i);
(ans*=Trie::Dfs(1,29,0))%=mod;
}
printf("%lld\n",ans);
}

觉得这篇文章怎么样?

点个赞,让更多人看到!

分享这篇文章

知识因分享而增值

分类

题解

标签

DP
Trie
数位DP
计数

版权声明:本文作者为 Nanami^2,首发于nanami7.top

遵循 CC BY-NC-SA 4.0 许可协议。转载请注明出处!

评论区

本评论区由 EveSunMaple 自主开发