您的位置:首页 > 其它

BZOJ 4569 [Scoi2016]萌萌哒

2016-09-11 16:00 232 查看
题目链接:http://www.lydsy.com/JudgeOnline/problem.php?id=4569

Description

一个长度为n的大数,用S1S2S3…Sn表示,其中Si表示数的第i位,S1是数的最高位,告诉你一些限制条件,每个条件表示为四个数,l1,r1,l2,r2,即两个长度相同的区间,表示子串Sl1Sl1+1Sl1+2…Sr1与Sl2Sl2+1Sl2+2…Sr2完全相同。比如n=6时,某限制条件l1=1,r1=3,l2=4,r2=6,那么123123,351351均满足条件,但是12012,131141不满足条件,前者数的长度不为6,后者第二位与第五位不同。问满足以上所有条件的数有多少个。

Input

第一行两个数n和m,分别表示大数的长度,以及限制条件的个数。接下来m行,对于第i行,有4个数li1,ri1,li2

,ri2,分别表示该限制条件对应的两个区间。

1≤n≤10^5,1≤m≤10^5,1≤li1,ri1,li2,ri2≤n;并且保证ri1-li1=ri2-li2。

Output

一个数,表示满足所有条件且长度为n的大数的个数,答案可能很大,因此输出答案模109+7的结果即可。

Sample Input

4 2

1 2 3 4

3 3 3 3

Sample Output

90

Solution

Cell: 这题看上去就是容斥

考虑没有任何限制时,因为不能有前导零,所以答案显然为10n-1*9。每添加一个限制,某些位的数字就捆绑在了一起,必须相同。则可以想到这样一个O(n2)暴力:把每一位看做一个节点,大力把l1~r1与l2~r2中对应的节点连起来,最后在每个联通块里的数字都必须是相同的。dfs一下记录联通块的个数为tot,则答案为10tot-1*9。

这个暴力怎么看都会超时的,因为每次连边太多太慢了。如果每次只要连很少的边,到最后再统计整理一下出答案就好了。貌似有个东西能很好的处理以上操作——ST表。

首先像ST表一样,建立出图0~logn。第i幅图的第j个节点表示大数中Sj~Sj+2i-1这个区间的数字。对于每个限制的区间,都可拆成长为2k的两个覆盖此区间的可重叠小区间。然后在第k幅图中把两个区间对应的小区间连起来。此处复杂度为O(n)。



处理好那m个条件后,只要把第1~logn幅图的联通信息转移到第0幅图,然后就跟暴力一样了。转移的方法也很简单,在第i幅图中对于每个联通块,若点a与点b有连边,则只需要在第i-1幅图中把a的前半个区间与b的前半个区间连起来,再把a的后半个区间与b的后半个区间连起来即可。每幅图最多有n个节点,一共logn幅图,所以复杂度为O(nlogn)。



Code



#include <cstdio>
#include <cstring>
#include <cctype>
#include <algorithm>
using namespace std;
#define N 100010
#define Log 17
#define mod 1000000007
#define foru(i,l,r) for (int i=l; i<=r; i++)
#define ford(i,r,l) for (int i=r; i>=l; i--)
#define fore(i,p,v) for (int i=cur[p], v=edge[i].v; i; i=edge[i].pre, v=edge[i].v)
#define read(x) (x=getint())
#define setmem(a,x) memset(a,x,sizeof(a))
typedef long long LL;

struct Tedge {int v,pre;};
int n,m,ans,lg
,bin
,id
,cnt,vst
;
struct Tgraph
{
Tedge edge[N*5];
int en,cur
;
void addedge(int u, int v);
void dfs(int p);
} graph[Log];

int getint()
{
char ch; int x;
while (!isdigit(ch=getchar()));
x=ch-'0';
while (isdigit(ch=getchar())) x=x*10+ch-'0';
return x;
}
void Tgraph::addedge(int u, int v)
{
edge[++en]=(Tedge){v,cur[u]}, cur[u]=en;
edge[++en]=(Tedge){u,cur[v]}, cur[v]=en;
}
void Tgraph::dfs(int p)
{
id[++cnt]=p, vst[p]=1;
fore(i,p,v)
if (!vst[v])
dfs(v);
}
LL pow(LL a, int b)
{
LL ans=1;
while (b)
{
if (b&1) ans=ans*a%mod;
b>>=1, a=a*a%mod;
}
return ans;
}
int main()
{
read(n), read(m);
foru(i,2,n) lg[i]=lg[i>>1]+1;
bin[0]=1;
foru(i,1,lg
) bin[i]=bin[i-1]<<1;
foru(i,1,m)
{
int l1=getint(), r1=getint(), l2=getint(), r2=getint(), j=lg[r1-l1+1];
graph[j].addedge(l1,l2);
graph[j].addedge(r1-bin[j]+1,r2-bin[j]+1);
}
ford(i,lg
,1)
{
setmem(vst,0);
for (int j=1; j+bin[i]-1<=n; j++)
if (!vst[j])
{
cnt=0;
graph[i].dfs(j);
foru(k,1,cnt-1)
{
graph[i-1].addedge(id[k],id[k+1]);
graph[i-1].addedge(id[k]+bin[i-1],id[k+1]+bin[i-1]);
}
}
}
setmem(vst,0);
int tot=0;
foru(i,1,n)
if (!vst[i])
{
tot++;
graph[0].dfs(i);
}
printf("%lld\n",pow(10,tot-1)*9%mod);
return 0;
}


若判断连通块用并查集的话能更快



#include <cstdio>
#include <cstring>
#include <cctype>
#include <algorithm>
#include <vector>
using namespace std;
#define N 1
b2a9
00010
#define Log 17
#define mod 1000000007
#define foru(i,l,r) for (int i=l; i<=r; i++)
#define ford(i,r,l) for (int i=r; i>=l; i--)
#define read(x) (x=getint())
#define setmem(a,x) memset(a,x,sizeof(a))
typedef long long LL;

int n,m,ans,lg
,bin
;
struct Tgraph
{
int fa
;
int getfa(int x);
void uni(int a, int b);
void init(int id);
} graph[Log];
vector<int> id
;

int getint()
{
char ch; int x;
while (!isdigit(ch=getchar()));
x=ch-'0';
while (isdigit(ch=getchar())) x=x*10+ch-'0';
return x;
}
int Tgraph::getfa(int x)
{
return fa[x]==x? x: fa[x]=getfa(fa[x]);
}
void Tgraph::uni(int a, int b)
{
fa[getfa(a)]=getfa(b);
}
void Tgraph::init(int id)
{
for (int i=1; i+bin[id]-1<=n; i++) fa[i]=i;
}
LL pow(LL a, int b)
{
LL ans=1;
while (b)
{
if (b&1) ans=ans*a%mod;
b>>=1, a=a*a%mod;
}
return ans;
}
int main()
{   read(n), read(m);
foru(i,2,n) lg[i]=lg[i>>1]+1;
bin[0]=1;
foru(i,1,lg
) bin[i]=bin[i-1]<<1;
foru(i,0,lg
) graph[i].init(i);
foru(i,1,m)
{
int l1=getint(), r1=getint(), l2=getint(), r2=getint(), j=lg[r1-l1+1];
graph[j].uni(l1,l2);
graph[j].uni(r1-bin[j]+1,r2-bin[j]+1);
}
ford(i,lg
,0)
{
for (int j=1; j+bin[i]-1<=n; j++) id[j].clear();
for (int j=1; j+bin[i]-1<=n; j++) id[graph[i].getfa(j)].push_back(j);
for (int j=1; i&&j+bin[i]-1<=n; j++)
foru(k,0,(int)id[j].size()-2)
{
graph[i-1].uni(id[j][k],id[j][k+1]);
graph[i-1].uni(id[j][k]+bin[i-1],id[j][k+1]+bin[i-1]);
}
}
int tot=0;
foru(i,1,n)
if (id[i].size())
tot++;
printf("%lld\n",pow(10,tot-1)*9%mod);
return 0;
}


把vector换成邻接表后又能快一点,代码就不放了

内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  BZOJ ST表