题目链接:luogu 3295
很明显的想法是用并查集维护最后有几个块内的数字必须相等,设最后有\(cnt\)个联通块,那么答案就是\(9\times10^{cnt-1}\)
暴力维护并查集是\(O(nlen)\)的,显然超时,考虑优化
记\(f_{i,j}\)表示区间\([i,i+2^j-1]\)的编号,编号相同则区间中元素值相同
考虑将一段区间\((l_1,r_1)\)和\((l_2,r_2)\)合并时,将每个区间拆成\(log\)段,合并\((l_1,l_1+2^k-1)\)和\((l_2,l_2+2^k-1)\)
最后像线段树标记下放一样将\(f_{i,j}\)的值下放到\(f_{i+2^j-1,j-1}\)和\(f_{i+2^j,j-1}\)
刚开始一只啊在想线段树优化然后。。。就凉了
#include<iostream>
#include<string.h>
#include<string>
#include<stdio.h>
#include<algorithm>
#include<math.h>
#include<vector>
#include<queue>
#include<map>
#include<set>
using namespace std;
#define lowbit(x) (x)&(-x)
#define rep(i,a,b) for (int i=a;i<=b;i++)
#define per(i,a,b) for (int i=a;i>=b;i--)
#define maxd 1000000007
typedef long long ll;
const int N=100000;
const double pi=acos(-1.0);
int n,m,fa[500100][20];
int read()
{
int x=0,f=1;char ch=getchar();
while ((ch<'0') || (ch>'9')) {if (ch=='-') f=-1;ch=getchar();}
while ((ch>='0') && (ch<='9')) {x=x*10+(ch-'0');ch=getchar();}
return x*f;
}
int find(int x,int y)
{
if (fa[x][y]==x) return x;
fa[x][y]=find(fa[x][y],y);
return fa[x][y];
}
int main()
{
n=read();m=read();
rep(i,1,n)
rep(j,0,19) fa[i][j]=i;
rep(i,1,m)
{
int l1=read(),r1=read(),l2=read(),r2=read();
int pos1=l1,pos2=l2;
per(j,19,0)
{
if (pos1+(1<<j)-1<=r1)
{
int fa1=find(pos1,j),fa2=find(pos2,j);
if (fa1!=fa2) fa[fa1][j]=fa2;
pos1+=(1<<j);pos2+=(1<<j);
}
}
}
per(j,19,1)
{
int i;
for (i=1;i+(1<<j)-1<=n;i++)
{
int fa1=find(i,j),fa2=find(i,j-1);
fa[fa2][j-1]=find(fa1,j-1);
int fa3=find(i+(1<<(j-1)),j-1);
fa[fa3][j-1]=find(fa1+(1<<(j-1)),j-1);
}
}
ll ans=1;
rep(i,1,n)
{
if (fa[i][0]==i)
{
if (ans==1) ans=(ans*9)%maxd;
else ans=(ans*10)%maxd;
}
}
printf("%lld",ans);
return 0;
}
原文地址:https://www.cnblogs.com/encodetalker/p/10801334.html
时间: 2024-09-30 19:12:45