BZOJ 4569 [Scoi2016]萌萌哒
2016-09-11 16:00
232 查看
题目链接:http://www.lydsy.com/JudgeOnline/problem.php?id=4569
,ri2,分别表示该限制条件对应的两个区间。
1≤n≤10^5,1≤m≤10^5,1≤li1,ri1,li2,ri2≤n;并且保证ri1-li1=ri2-li2。
1 2 3 4
3 3 3 3
考虑没有任何限制时,因为不能有前导零,所以答案显然为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)。
![](https://img-blog.csdn.net/20160911163703917)
处理好那m个条件后,只要把第1~logn幅图的联通信息转移到第0幅图,然后就跟暴力一样了。转移的方法也很简单,在第i幅图中对于每个联通块,若点a与点b有连边,则只需要在第i-1幅图中把a的前半个区间与b的前半个区间连起来,再把a的后半个区间与b的后半个区间连起来即可。每幅图最多有n个节点,一共logn幅图,所以复杂度为O(nlogn)。
![](https://img-blog.csdn.net/20160911165152564)
![](https://img-blog.csdn.net/20160911165754979)
若判断连通块用并查集的话能更快
![](https://img-blog.csdn.net/20160911170329083)
把vector换成邻接表后又能快一点,代码就不放了
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 21 2 3 4
3 3 3 3
Sample Output
90Solution
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 4569: [Scoi2016]萌萌哒 [并查集 倍增]
- bzoj 4569: [Scoi2016]萌萌哒
- BZOJ4569 [Scoi2016]萌萌哒
- bzoj 4569: [Scoi2016]萌萌哒
- [BZOJ4569][SCOI2016]萌萌哒 并查集+倍增
- [BZOJ4569][Scoi2016]萌萌哒(并查集+st表)
- 【bzoj 4569】[Scoi2016]萌萌哒 (并查集)
- BZOJ 4569: [Scoi2016]萌萌哒【倍增并查集
- [BZOJ4569][SCOI2016]萌萌哒
- [BZOJ 4569][SCOI 2016] 萌萌哒 区间并查集(ST表思想)
- [BZOJ]4569 [SCOI2016] 萌萌哒 并查集神题
- 【bzoj4569】【SCOI2016】【萌萌哒】【st表+并查集】
- bzoj4569: [Scoi2016]萌萌哒
- BZOJ 4569 【SCOI2016】 萌萌哒
- 【BZOJ4569】[Scoi2016]萌萌哒(并查集+st表)
- 【BZOJ 4569】【SCOI 2016】萌萌哒
- bzoj4569: [Scoi2016]萌萌哒【稀疏表+并查集】
- BZOJ 4569: [Scoi2016]萌萌哒 ST表 并查集
- BZOJ 4569: [Scoi2016]萌萌哒 并查集+倍增
- bzoj 4569: [Scoi2016]萌萌哒