【BZOJ4569】萌萌噠

題目鏈接

BZOJ4569

解法:倍增+並查集

先想暴力的做法。每次讀入l1,r1,l2,r2l_1,r_1,l_2,r_2,令ii00枚舉到r1l1r_1-l_1,然後用並查集合並l1+il_1+il2+il_2+i,最後統計並查集中聯通塊的個數ss,答案即爲9×10s19×10^{s-1}
但是這樣會TLE。
對暴力進行優化。更改並查集中數組ff的含義,令fx,kf_{x,k}表示從xxx+2k1x+2^k-1這兩段的共同編號,亦即若fx,k=fy,kf_{x,k}=f_{y,k}則表示fx,,fx+2k1f_x,\dots,f_{x+2^k-1}fy,,fy+2k1f_y,\dots,f_{y+2^k-1}這兩段數每項分別處於相同的聯通塊中。合併時用倍增的處理方式(參考ST表)。

代碼

#include<iostream>
#include<cstdio>
#include<cmath>

using namespace std;

const long long p=1000000007ll;
long long ans=1ll;
int n,m,l1,l2,r1,r2,f[17][100001];
bool flag=false;

int find(int x,int k){
	return f[k][x]==x?x:f[k][x]=find(f[k][x],k);
}

void merge(int x,int y,int k){
	if(y>100000)return;
	int fx=find(x,k),fy=find(y,k);
	if(fx!=fy)f[k][fx]=fy;
}

void update(int a,int b,int c,int d){
	int k=(int)log2(b-a+1);
	merge(a,c,k),merge(b-(1<<k)+1,d-(1<<k)+1,k);
}

int main(){
	scanf("%d%d",&n,&m);int l=(int)log2(n);
	for(int i=1;i<=n;++i)for(int k=0;k<17;++k)f[k][i]=i;
	for(int i=1;i<=m;++i)scanf("%d%d%d%d",&l1,&r1,&l2,&r2),update(l1,r1,l2,r2);
	for(int i=l;i;--i)for(int j=1;j<=n-(1<<i)+1;++j){int f=find(j,i);merge(j,f,i-1),merge(j+(1<<i-1),f+(1<<i-1),i-1);}
	for(int i=1;i<=n;++i)if(find(i,0)==i)(ans*=(flag?10:((flag=true),9)))%=p;
	printf("%lld",ans);
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章