[SCOI2016]萌萌哒

https://www.luogu.com.cn/problem/P3295

题面

求字符串的个数,满足有若干种形如\(l1,r1,l2,r2\)的要求,使\(S_{l1,r1}=S_{l2,r2}\)

分析

显然,如果知道哪些字符相同,答案为\(9\times 10^{size-1}\)
考虑用并查集维护
\(\because\) 该操作具有可加性和可分裂性
\(\therefore\) 一个神仙的想法:倍增
可以将其分成若干层

\[[1,1+2^k),[2,2+2^k),\cdot\cdot\cdot \\ [1,1+2^{k-1}),[2,2+2^{k-1}),\cdot\cdot\cdot\\ [1,1+2^{k-2}),[2,2+2^{k-2}),\cdot\cdot\cdot\\ \cdot\cdot\cdot\cdot\cdot\cdot \]

然后对每一层维护一个并查集,表示该区间与另一端区间是否完全相同,然后从上往下做一遍即可

#include<bits/stdc++.h>
#define ll long long
const int p=1e9+7;
using namespace std;

const int N=2e5+5;
int n,m,ans;

struct A{
	int f[N];	
	inline int find(int x) {
		int r=x,u;
		while(f[r]!=r) {
			r=f[r];
		}
		while(x!=r) {
			u=x;
			x=f[x];
			f[u]=r;
		}
		return r;
	}
	inline void mer(int x,int y) {
		int u=find(x),v=find(y);
		if(u!=v) f[v]=u;
	}
}tr[20];

int main() {
	scanf("%d%d",&n,&m);
	for(int i=0;i<=16;i++) {
		for(int j=1;j<=n;j++) {
			tr[i].f[j]=j;
		}
	}
	for(int i=1;i<=m;i++) {
		int l1,r1,l2,r2; scanf("%d%d%d%d",&l1,&r1,&l2,&r2);
		for(int j=16;j>=0;j--) {
			if(l1+(1<<j)-1<=r1) {
				tr[j].mer(l1,l2);
				l1+=(1<<j),l2+=(1<<j);
			}
		}
	}
	for(int i=16;i;i--) {
		for(int j=1;j<=n;j++) {
			int r=tr[i].find(j);
			tr[i-1].mer(r,j),tr[i-1].mer(r+(1<<i-1),j+(1<<i-1));
		}
	}
	for(int i=1;i<=n;i++) {
		if(tr[0].find(i)==i) ans=!ans?9:(ll)ans*10%p;
	}
	printf("%d\n",ans);
	return 0;
}
posted @ 2021-01-03 20:02  wwwsfff  阅读(94)  评论(0)    收藏  举报