成功的(看了一吨题解之后),我A掉了第二道紫题。
好,我们仔细观察,发现这是一个排列组合问题。
有些限定条件,要相等的地方,我们就用并查集并起来。最后一查有多少个并查集,就有多少个位置可供自由选择。
所以答案就是10^(并查集数),去除前导0:*(9/10)
好,这样我们得到了一个O(mn)算法。
然后我们考虑优化:每个区间可能被合并多次。所以我们有两种选择:线段树/ST表。
考虑到这是ST表例题(???????),我们就来个ST表与并查集联动求解...
我们的ufs[i][j]代表在[i][2^j]这个区间内的情况。
然后每次合并的时候都往下合并两个j-1(也可以最后再一起下传标记)
实质上是开了logn个并查集,因为我发现find和merge都不跨层。
题外话:与RE战斗的艰辛历程
交了11次RE,实在是让人感受绝望啊。
两种方法全都RE,所幸刚才我写的时候都查出来错了。
那么先来看看第一种方法:
每次都跟线段树一样恰好标记完最少的节点,最后所有标记一起下传。
1 #include <cstdio> 2 using namespace std; 3 const int N = 100010; 4 const int mo = 1000000007; 5 6 int ufs[N][30],n,m; 7 int find(int x,int j) 8 { 9 if(ufs[x][j]!=x) ufs[x][j]=find(ufs[x][j],j); 10 return ufs[x][j]; 11 } 12 void merge(int x,int y,int j) 13 { 14 ufs[find(x,j)][j]=find(y,j);///->!!这里调了一个错,之前是ufs[x][j]=...... 15 return; 16 } 17 18 19 int main() 20 { 21 scanf("%d%d",&n,&m); 22 for(int j=0;j<=29;j++) 23 { 24 for(int i=1;i<=n;i++) ufs[i][j]=i;///->! 25 } 26 int a,b,c,d; 27 int md=0; 28 while((1<<md)<=n) md++; 29 md--; 30 for(int i=1;i<=m;i++) 31 { 32 scanf("%d%d%d%d",&a,&b,&c,&d); 33 for(int j=md;j>=0;j--) 34 { 35 if(a+(1<<j)-1<=b) merge(a,c,j),a+=(1<<j),c+=(1<<j); 36 } 37 } 38 /// 39 for(int j=md;j>=1;j--)///这里,RE的罪魁祸首!我之前写的0,结果传进去个j=-1直接挂 40 { 41 for(int i=1;i+(1<<j)-1<=n;i++) 42 { 43 merge(i,find(i,j),j-1); 44 merge(i+(1<<(j-1)),find(i,j)+(1<<(j-1)),j-1); 45 } 46 } 47 /// 48 long long ans=9; 49 bool q=false; 50 for(int i=1;i<=n;i++) 51 { 52 if(find(i,0)==i) 53 { 54 if(q) ans=(ans*10)%mo; 55 q=1; 56 } 57 } 58 //for(int i=1;i<=n;i++) printf("%d ",find(i,0)); 59 printf("%lld",ans); 60 return 0; 61 }