Luogu P3295 [SCOI2016]萌萌哒

Link
一个很trivial的想法是并查集暴力维护,最后的答案是\(9*10^n\),\(n\)是最后剩下的并查集的个数。
因为每次连边的都是一段区间到另一端区间,所以我们可以二进制拆分,把并查集的每个点拆成\(\log\)个点,每次把要连边的区间拆成\(\log\)个点直接连,最后再还原。

#include<cstdio>
#include<cctype>
#include<numeric>
#include<algorithm>
using std::iota;
const int N=100007,P=1000000007;
int read(){int x=0,c=getchar();while(!isdigit(c))c=getchar();while(isdigit(c))x=x*10+c-48,c=getchar();return x;}
int mod(int a){return a+(a>>31&P);}
int inc(int a,int b){return mod(a+b-P);}
int dec(int a,int b){return mod(a-b);}
int mul(int a,int b){return 1ll*a*b%P;}
int power(int a,int k){int r=1;for(;k;k>>=1,a=mul(a,a))if(k&1)r=mul(a,r);return r;}
int n,m,cnt,id[20][N],Log[N],vis[N],fa[20*N];
int find(int x){return x==fa[x]? x:fa[x]=find(fa[x]);}
void merge(int u,int v){fa[find(u)]=find(v);}
void get(int x,int&y){x%=n,y=x?x:n;}
int main()
{
    n=read(),m=read();int ans=0;
    for(int i=2;i<=n;++i) Log[i]=Log[i>>1]+1;
    for(int i=0;i<=Log[n];++i) for(int j=1;j<=n;++j) id[i][j]=++cnt;
    iota(fa+1,fa+cnt+1,1);
    for(int i=1;i<=m;++i)
    {
    int l=read(),r=read(),len=read(),d=read();
    d=len-l,len=r-l+1;
    for(int k=0;len;len>>=1,++k) if(len&1) merge(id[k][l+((len>>1)<<(k+1))],id[k][l+((len>>1)<<(k+1))+d]);
    }
    for(int i=Log[n];i;--i)
    for(int j=1,f,x;j+(1<<i)-1<=n;++j)
    {
        if((f=find(id[i][j]))==id[i][j])continue;
        get(f,x),merge(id[i-1][j],id[i-1][x]),merge(id[i-1][j+(1<<(i-1))],id[i-1][x+(1<<(i-1))]);
    }
    for(int i=1,x;i<=n;++i)
    {
    get(find(id[0][i]),x);
        if(!vis[x]) vis[x]=1,++ans;
    }
    printf("%d",mul(9,power(10,ans-1)));
}

原文地址:https://www.cnblogs.com/cjoierShiina-Mashiro/p/12207753.html

时间: 2024-08-29 19:48:55

Luogu P3295 [SCOI2016]萌萌哒的相关文章

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} \

luogu P3295 [SCOI2016]萌萌哒 |倍增+并查集

题目描述 一个长度为 nnn 的大数,用 S1S2S3?SnS_1S_2S_3 \cdots S_nS1?S2?S3??Sn?表示,其中 SiS_iSi? 表示数的第 iii 位, S1S_1S1? 是数的最高位.告诉你一些限制条件,每个条件表示为四个数,l1,r1,l2,r2l_1,r_1,l_2,r_2l1?,r1?,l2?,r2?,即两个长度相同的区间,表示子串Sl1Sl1+1Sl1+2?Sr1S_{l_1}S_{l_1+1}S_{l_1+2} \cdots S_{r_1}Sl1??Sl1

luogu 3295[SCOI2016]萌萌哒

题目链接: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

p3295 [SCOI2016]萌萌哒

传送门 分析 我们可以将一个点拆成logN个点,分别代表从点i开始,长度为2^k的子串 那么当我们处理两个区间相等的关系时,对区间做二进制拆分,拆成log个区间,分别并起来即可 当然我们这样做修改是省心了,但是同时查询的时候也会带来一些麻烦--因为,我们要求的信息是最底层的,只能是长度为1的区间,而不能有奇奇怪怪的区间 不过没关系,我们这时运用等式1,拆分并查集 具体来讲,我们从最长的区间开始逐个枚举,每次查找他和他的父亲,然后把它和父亲都劈成两半,前一半和前一半连边,后一半和后一半连边即可,这

【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不满足条件,前者数

bzoj 4569: [Scoi2016]萌萌哒

4569: [Scoi2016]萌萌哒 Description 一个长度为n的大数,用S1S2S3...Sn表示,其中Si表示数的第i位,S1是数的最高位,告诉你一些限制条件,每个条 件表示为四个数,l1,r1,l2,r2,即两个长度相同的区间,表示子串Sl1Sl1+1Sl1+2...Sr1与Sl2Sl2+1Sl2+2...S r2完全相同.比如n=6时,某限制条件l1=1,r1=3,l2=4,r2=6,那么123123,351351均满足条件,但是12012,13 1141不满足条件,前者数的

luogu P3291 [SCOI2016]妖怪

二次联通门 : luogu P3291 [SCOI2016]妖怪 /* luogu P3291 [SCOI2016]妖怪 凸包 我果然还是naive 看见这题就想二分 结果惨挂 做了凸包后就想三分 又挂了.. 完美落入了每个坑..果然还是自己太菜 对于每个妖怪,其在(a,b)时的最大战力为过当前点斜率为(-b/a)的直线的截距之和 最大的战力就是最外面的那条直线 对n个点做一个上凸包 最大值一定在凸包的右上部分(自行脑补) 那么这些点的斜率都有着一个范围ki-1<ki<ki+1 那么战力就变为

4569: [Scoi2016]萌萌哒

4569: [Scoi2016]萌萌哒 链接 分析: 每次给出的两个区间长度是一样的,对应位置的数字也是一样的,那么可以将两两对应的数字用并查集合并,设最后有$cnt$个不同的集合,答案就是$9\times 10 ^{cnt-1}$,第一个数不能是0. 暴力合并太慢了,考虑优化.对于一段区间,用倍增的思想分成log段,分别合并log段,最后的下放一下标记即可.类似线段树的懒标记. 代码: #include<cstdio> #include<algorithm> #include&l

SCOI2016萌萌哒题解

SCOI2016萌萌哒题解 题目链接 思路 这个题目大概就是给你一些限制,使区间相等,那么一个很巧妙的思路就是将区间限制转化为两个店在一个并查集里 我们要求的是只有一位的并查集的个数,答案为 ( t我们要求的是只有一位的并查集的个数) 但直接操作复杂度过大 于是有人想到了倍增 f[i][j]表示区间[ , ]所在的并查集 最开始时记录每个大区间的左右儿子 输入 l1 l2 r1 r2时像ST表一样合并 再分别处理每个大区间,如果f[i][j]和f[a][b]在一个并查集里,那么他们的左右儿子也在