1. 程式人生 > 其它 >T184176-「EZEC-10」序列【Trie】

T184176-「EZEC-10」序列【Trie】

正題

題目連結:https://www.luogu.com.cn/problem/T184176


題目大意

求有多少個長度為\(n\)的序列\(a\)滿足,都在\([0,k]\)的範圍內且滿足\(m\)個限制刑如:\(a_x\ xor\ a_y=z\)

\(0\leq n,m\leq 5\times 10^5,0\leq k<2^{30}\)


解題思路

首先假設有合法方案,那麼對於一個位置\(a_x\)確定之後與它直接或間接限制的\(a_y\)都將被確定。

我們可以設限制為一條邊,然後先\(dfs\)判斷一次是否限制之間沒有衝突。

然後考慮對於每個聯通塊我們隨意找到一個位置\(x\),那麼其他的點都將被表達為\(a_x\ xor\ w\)

的形式。

然後我們要求找到有多少個\(a_x\)滿足對於所有的\(w\)都有\(a_x\ xor\ w\leq k\)

這個可以用\(Trie\)數來做,每次封閉的是一個子樹,直接處理就好了。

時間複雜度\(O(n\log k)\)


code

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<stack>
#define ll long long
using namespace std;
const ll N=5e5+10,P=1e9+7;
struct node{
	ll to,next,w;
}a[N<<1];
ll n,m,k,tot,ls[N],z[N];
ll cnt,t[N][2],res,ans=1;
bool v[N];stack<ll > s;
void addl(ll x,ll y,ll w){
	a[++tot].to=y;
	a[tot].next=ls[x];
	ls[x]=tot;a[tot].w=w;
	return;
}
bool dfs(ll x){
	v[x]=1;s.push(z[x]);
	for(ll i=ls[x];i;i=a[i].next){
		ll y=a[i].to;
		if(v[y]){
			if((z[x]^a[i].w)!=z[y])
				return 1;
		}
		else{
			z[y]=z[x]^a[i].w;
			if(dfs(y))return 1;	
		}
	}
	return 0;
}
void Limit(ll &x,ll w,ll p){
	if(x==-1||p<0)return;
	if(!x){x=++cnt;t[x][0]=t[x][1]=0;}
	if((k>>p)&1)Limit(t[x][(w>>p)&1^1],w,p-1);
	else{
		t[x][(w>>p)&1^1]=-1;
		Limit(t[x][(w>>p)&1],w,p-1);
	}
	return;
}
void Count(ll x,ll L,ll R){
	if(L>k)return;
	if(x==-1)res-=min(R,k)-L+1;
	if(x<=0)return;
	ll mid=(L+R)>>1;
	Count(t[x][0],L,mid);
	Count(t[x][1],mid+1,R);
	return;
}
signed main()
{
	scanf("%lld%lld%lld",&n,&m,&k);
	for(ll i=1;i<=m;i++){
		ll x,y,w;
		scanf("%lld%lld%lld",&x,&y,&w);
		addl(x,y,w);addl(y,x,w);
	}
	res=0;
	for(ll i=1;i<=n;i++){
		if(v[i])continue;cnt=t[0][0]=0;
		if(dfs(i))return puts("0")&0;
		while(!s.empty())Limit(t[0][0],s.top(),29),s.pop();
		res=k+1;Count(1,0,(1<<30)-1);
		ans=ans*res%P;
	}
	printf("%lld\n",ans);
	return 0;
}