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,l_2+2^k-1)\)

最后像线段树标记下放一样将\(f_{i,j}\)的值下放到\(f_{i+2^j-1,j-1}\)和\(f_{i+2^j,j-1}\)

刚开始一只啊在想线段树优化然后。。。就凉了

#include<iostream>
#include<string.h>
#include<string>
#include<stdio.h>
#include<algorithm>
#include<math.h>
#include<vector>
#include<queue>
#include<map>
#include<set>
using namespace std;
#define lowbit(x) (x)&(-x)
#define rep(i,a,b) for (int i=a;i<=b;i++)
#define per(i,a,b) for (int i=a;i>=b;i--)
#define maxd 1000000007
typedef long long ll;
const int N=100000;
const double pi=acos(-1.0);
int n,m,fa[500100][20];

int read()
{
    int x=0,f=1;char ch=getchar();
    while ((ch<'0') || (ch>'9')) {if (ch=='-') f=-1;ch=getchar();}
    while ((ch>='0') && (ch<='9')) {x=x*10+(ch-'0');ch=getchar();}
    return x*f;
}

int find(int x,int y)
{
    if (fa[x][y]==x) return x;
    fa[x][y]=find(fa[x][y],y);
    return fa[x][y];
}

int main()
{
    n=read();m=read();
    rep(i,1,n)
        rep(j,0,19) fa[i][j]=i;
    rep(i,1,m)
    {
        int l1=read(),r1=read(),l2=read(),r2=read();
        int pos1=l1,pos2=l2;
        per(j,19,0)
        {
            if (pos1+(1<<j)-1<=r1)
            {
                int fa1=find(pos1,j),fa2=find(pos2,j);
                if (fa1!=fa2) fa[fa1][j]=fa2;
                pos1+=(1<<j);pos2+=(1<<j);
            }
        }
    }
    per(j,19,1)
    {
        int i;
        for (i=1;i+(1<<j)-1<=n;i++)
        {
            int fa1=find(i,j),fa2=find(i,j-1);
            fa[fa2][j-1]=find(fa1,j-1);
            int fa3=find(i+(1<<(j-1)),j-1);
            fa[fa3][j-1]=find(fa1+(1<<(j-1)),j-1);
        }
    }
    ll ans=1;
    rep(i,1,n)
    {
        if (fa[i][0]==i)
        {
            if (ans==1) ans=(ans*9)%maxd;
            else ans=(ans*10)%maxd;
        }
    }
    printf("%lld",ans);
    return 0;
}

原文地址:https://www.cnblogs.com/encodetalker/p/10801334.html

时间: 2024-09-30 19:12:45

luogu 3295[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 P3295 [SCOI2016]萌萌哒

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

【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]在一个并查集里,那么他们的左右儿子也在

[SCOI2016]萌萌哒 解题报告

[SCOI2016]萌萌哒 题意 有一个长度为 \(n\) 的大数, 有 \(m\) 个形如 l1,r1,l2,r2的限制, 表示区间 \([l1,r1]\) 和 \([l2,r2]\) 完全相等, 求满足这些限制的数的个数, 不能含有前导零. \((1 \le n,m \le 10^5)\) 思路 暴力 : 直接 \(O(nm)\) 把相等的数合成一个并查集, 最后若并查集的数量为 \(k\), 则答案为 \(9 \times 10^{k-1}\). (不能有前导零). 考虑优化. 首先想到线