P3295 萌萌哒 并查集RMQ联动

又切一道紫题!!!

成功的(看了一吨题解之后),我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 }

AC代码,跑的比下面快一些

第二种思路:

每次都传到底。如果已经在一起就不往下推了。

 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 ffind(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     if(j<0) return 0;
12     int ans=x,k;
13     while(ufs[ans][j]!=ans) ans=ufs[ans][j];
14     while(ufs[x][j]!=x)
15     {
16         k=ufs[x][j];
17         ufs[x][j]=ans;
18         x=k;
19     }
20     return ans;
21 }
22 void mmerge(int x,int y,int j)
23 {
24     if(j<0) return;///这里控制情况,AC
25     if(ffind(x,j)==ffind(y,j)) return;
26     ufs[ffind(x,j)][j]=ffind(y,j);///->!!
27     mmerge(x,y,j-1),mmerge(x+(1<<(j-1)),y+(1<<(j-1)),j-1);///这里RE!会传入j=-1
28     return;
29 }
30
31
32 int main()
33 {
34     scanf("%d%d",&n,&m);
35     for(int j=0;j<=29;j++)
36     {
37         for(int i=1;i<=n;i++) ufs[i][j]=i;///->!
38     }
39     int a,b,c,d;
40     int md=0;
41     while((1<<md)<=n) md++;
42     md--;
43     for(int i=1;i<=m;i++)
44     {
45         scanf("%d%d%d%d",&a,&b,&c,&d);
46         for(int j=md;j>=0;j--)
47         {
48             if(a+(1<<j)-1<=b) mmerge(a,c,j),a+=(1<<j),c+=(1<<j);
49         }
50     }
51     /**
52
53     */
54     long long ans=9;
55     bool q=false;
56     for(int i=1;i<=n;i++)
57     {
58         if(ffind(i,0)==i)
59         {
60             if(q) ans=(ans*10)%mo;
61             q=1;
62         }
63     }
64     //for(int i=1;i<=n;i++) printf("%d ",find(i,0));
65     printf("%lld",ans);
66     return 0;
67 }

AC代码

结论:函数里最好写上特判违法情况,保险。

原文地址:https://www.cnblogs.com/huyufeifei/p/8718999.html

时间: 2024-08-02 12:54:09

P3295 萌萌哒 并查集RMQ联动的相关文章

Luogu P3295 [SCOI2016]萌萌哒(并查集+倍增)

P3295 [SCOI2016]萌萌哒 题面 题目描述 一个长度为 \(n\) 的大数,用 \(S_1S_2S_3 \cdots S_n\) 表示,其中 \(S_i\) 表示数的第 \(i\) 位, \(S_1\) 是数的最高位.告诉你一些限制条件,每个条件表示为四个数, \(l_1,r_1,l_2,r_2\) ,即两个长度相同的区间,表示子串 \(S_{l_1}S_{l_1+1}S_{l_1+2} \cdots S_{r_1}\) 与 \(S_{l_2}S_{l_2+1}S_{l_2+2} \

【BZOJ-4569】萌萌哒 ST表 + 并查集

4569: [Scoi2016]萌萌哒 Time Limit: 10 Sec  Memory Limit: 256 MBSubmit: 459  Solved: 209[Submit][Status][Discuss] Description 一个长度为n的大数,用S1S2S3...Sn表示,其中Si表示数的第i位,S1是数的最高位,告诉你一些限制条件,每个条件表示为四个数,l1,r1,l2,r2,即两个长度相同的区间,表示子串Sl1Sl1+1Sl1+2...Sr1与Sl2Sl2+1Sl2+2.

四川oi 萌萌哒 (分层并查集)

萌萌哒 时间限制: 1 Sec  内存限制: 256 MB提交: 12  解决: 2[提交][状态][讨论版] 题目描述 一个长度为 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,

【BZOJ4569】[Scoi2016]萌萌哒 倍增+并查集

[BZOJ4569][Scoi2016]萌萌哒 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不满足条件,前者数

HDU 2874 LCA转RMQ+并查集

点击打开链接 题意:问两个城市是否相连,不相连输出Not connected,否则输出两个城市间的最短距离 思路:用并查集判断两个城市的连通性,如果联通则做法和LCA一样,但是注意的一点是地图不连通的话,我们要将所有点都建起来,就要加一个模拟的点,将所有图串起来,很好处理的,看一下就会了 #include <vector> #include <stdio.h> #include <string.h> #include <stdlib.h> #include

【BZOJ4569】萌萌哒(并查集,倍增)

[BZOJ4569]萌萌哒(并查集,倍增) 题面 BZOJ 题意: 有一个长度为\(n\)的数 给定\(m\)个限制条件 每次限制\(l1-r1\)与\(l2-r2\)是相同的 求出方案数 题解 如果每次给定的限制都是告诉你某一位和某一位是相同的 那么,我们的做法是: 并查集,然后计算有\(k\)个联通块 \(ans=9*10^{k-1}\) 但是,现在每次给定的都是一个区间 我们不太可能暴力的把区间之间的位置两两进行一次合并 所以,我们来想个办法优化一下. 试试倍增? 维护\(logn\)个并

[SCOI2016]萌萌哒(倍增+并查集)

当区间\([a,b]\)和\([c,d]\)对应相等时. 我们把两个区间对应位置上的数所在并查集合并. 最后并查集的数量为\(num\)答案就是\(9*10^num\)因为是个数,不能有前置\(0\). 但是两个区间对应位置上的数所在并查集合并太浪费时间. 怎么办. 考虑使用倍增. 我们用\((i,j)\)代表\([i,i+(1<<j)-1]\)这个区间然后任何一个区间最多可以\(log\)个这样的倍增的区间拼起来. 然后呢? 我们按倍增区间的大小从大往小枚举.当\((x,i)\)和\((y,

习题:过路费(kruskal+并查集+LCA)

过路费  [问题描述]在某个遥远的国家里,有 n 个城市.编号为 1,2,3,…,n.这个国家的政府修 建了 m 条双向道路,每条道路连接着两个城市.政府规定从城市 S 到城市 T 需 要收取的过路费为所经过城市之间道路长度的最大值.如:A 到 B 长度为 2,B 到 C 长度为 3,那么开车从 A 经过 B 到 C 需要上交的过路费为 3. 佳佳是个做生意的人,需要经常开车从任意一个城市到另外一个城市,因此 他需要频繁地上交过路费,由于忙于做生意,所以他无时间来寻找交过路费最低 的行驶路线.然

Uva 12361 File Retrieval 后缀数组+并查集

题意:有F个单词,1 <= F <=60 , 长度<=10^4, 每次可以输入一个字符串,所有包含该字串的单词会形成一个集合. 问最多能形成多少个不同的集合.集合不能为空. 分析:用后缀数组处理.然后首先考虑一个单词形成一个集合的情况,若该单词是其他单词的字串,则该单词显然不会形成一个集合,那么利用后缀数组, 对于每个单词看能否与其他单词有LCP,且LCP 长度为该单词本身长度. 然后就是多个单词形成集合的情况:比较简单的处理方式就是将h数组值相同的下标集中存储,比如h[x] = h[y