「SCOI2016」萌萌哒

「SCOI2016」萌萌哒

题目描述

一个长度为 \(n\) 的大数,用 \(S_1S_2S_3 \ldots 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} \ldots S_{r_1} $与 \(S_{l_2}S_{l_2 + 1}S_{l_2 + 2} \ldots S_{r_2}\)完全相同。

比如 \(n = 6\) 时,某限制条件 $(l_1 = 1, r_1 = 3, l_2 = 4, r_2 = 6) $,那么 \(123123\)、\(351351\) 均满足条件,但是 \(12012\)、\(131141\) 不满足条件,前者数的长度不为 \(6\),后者第二位与第五位不同。问满足以上所有条件的数有多少个。

\(1 \leq n \leq 10^5, 1 \leq m \leq 10^5,1 \leq li_1,ri_1,li_2,ri_2 \leq n\) 并且保证 ${r_i}_1 - {l_i}_1 = {r_i}_2 - {l_i}_2 $

解题思路 :

观察发现,限制条件的本质是使得一些位置只能填同样的字符,我们不妨把限制在一起的位置看做点,在它们之间连一条边

那么统一联通块里面的位置就只能填同一字符了,所以设联通快数为 \(x\) ,那么答案就是 \(9 \times 10^{x-1}\) (第一位不能填 \(0\) ,所以少一种选择)

于是就有一个暴力的做法,用并查集维护联通块,每次对于一组限制 \(l_1, r_1, l_2, r_2\) ,暴力将两个区间的对应点合并,复杂度 \(O(n^2logn)\)

考虑怎么优化并查集的合并,由于是区间问题,所以很容易就想到用线段树或者 \(st\) 表来维护

每次把可以询问区间拆成 \(log\) 个区间,区间与区间之间进行连边,难点在于最后怎么将区间之间的合并转化到点上

由于题目只需要最终询问一次,不妨利用 \(lazytag\) 的思想,对每一种长度的区间用一个并查集来维护,最后算答案的时候将合并信息下传

具体来讲,考虑 \(st\) 表的做法:设 \(fa(i,j)\) 表示左端点为 \(i\) 的长度为 \(2 ^ j\) 的区间所在集合的 \(root\) 的左端点

那么下传信息的时候只需要 \((i, j-1), (i+2^{j-1},j-1)\) 分别和 \((fa(i, j), j - 1)\) 合并即可,最后统计一下 \((i, 0)\) 的联通块数 ,总复杂度 \(O(nlog^2n)\)

/*program by mangoyang*/
#include<bits/stdc++.h>
#define inf (0x7f7f7f7f)
#define Max(a, b) ((a) > (b) ? (a) : (b))
#define Min(a, b) ((a) < (b) ? (a) : (b))
typedef long long ll;
using namespace std;
template <class T>
inline void read(T &x){
    int f = 0, ch = 0; x = 0;
    for(; !isdigit(ch); ch = getchar()) if(ch == '-') f = 1;
    for(; isdigit(ch); ch = getchar()) x = x * 10 + ch - 48;
    if(f) x = -x;
}

const int N = 500005, Mod = 1000000007;
int fa[N][21], n, m;

inline int ask(int x, int y){
    return x == fa[x][y] ? x : fa[x][y] = ask(fa[x][y], y);
}

int main(){
    read(n), read(m);
    for(int i = 1; i <= n; i++)
        for(int j = 0; j <= 20; j++) fa[i][j] = i;  

    while(m--){
        int l1, r1, l2, r2;
        read(l1), read(r1), read(l2), read(r2);
        int ps1 = l1, ps2 = l2;
        for(int i = 20; ~i; i--)
            if(ps1 + (1 << i) - 1 <= r1){
                int p = ask(ps1, i), q = ask(ps2, i);
                if(p != q) fa[p][i] = q;
                ps1 += (1 << i), ps2 += (1 << i);
            }
    }
    for(int j = 20; j; j--)
        for(int i = 1; i + (1 << j) - 1 <= n; i++){
            int p = ask(i, j), q = ask(i, j - 1);
            fa[q][j-1] = ask(p, j - 1);
            p = ask(i, j), q = ask(i + (1 << j - 1), j - 1);
            fa[q][j-1] = ask(p + (1 << j - 1), j - 1);
        }

    ll res = 1ll;
    for(int i = 1; i <= n; i++)
        if(fa[i][0] == i) (res *= (res == 1ll) ? 9ll : 10ll) %= Mod;
    cout << res;
    return 0;
}

原文地址:https://www.cnblogs.com/mangoyang/p/9484338.html

时间: 2024-08-25 10:51:30

「SCOI2016」萌萌哒的相关文章

LibreOJ #2012. 「SCOI2016」背单词

二次联通门 : LibreOJ #2012. 「SCOI2016」背单词 /* LibreOJ #2012. 「SCOI2016」背单词 Trie + 贪心 大家都吐槽题目反人类 可我觉得还好,毕竟见的多了 不会做啊.. 正解好巧妙 考虑一下,发现一操作完全不必要,可以省去 因为所有的字符串的后缀关系会形成一个树 那么把字符串倒序插入Trie中 建树,每次向子树小的一个点转移即可 */ #include <cstdio> #include <algorithm> #include

AC日记——「SCOI2016」背单词 LiBreOJ 2012

#2012. 「SCOI2016」背单词 思路: Orz: 代码: #include <bits/stdc++.h> using namespace std; #define maxn 100005 #define maxm 510005 int n,ch[maxm][26],tot=1,len,head[maxm],E[maxm],V[maxm],cnt=1; int val[maxm],cnt2,size[maxm],sta[maxm],top; long long ans,sum; ch

LibreOJ #2016. 「SCOI2016」美味

二次联通门 : LibreOJ #2016. 「SCOI2016」美味 /* LibreOJ #2016. 「SCOI2016」美味 dalao们都在说这题如果没有加法balabala就可以用可持久化trie解决了 然而我连那个也不会啊QAQ 此题用主席树 从高位到低位贪心 能填1就填1,也就是查询一段区间有没有某个范围的数 (然而由乃dalao说可持久化线段树和可持久化trie是一个东西) */ #include <cstdio> #include <iostream> #inc

LibreOJ #2013. 「SCOI2016」幸运数字

二次联通门 : LibreOJ #2013. 「SCOI2016」幸运数字 /* LibreOJ #2013. 「SCOI2016」幸运数字 树链剖分 + 线段树 + 线性基合并 没什么可说的 对原树进行树链剖分 然后建线段树 每个区间维护一段线性基 每次暴力把一段插入另一段中 最后线性基求最大即可 注意线性基求最大时一定是倒着枚举的 */ #include <cstdio> #include <iostream> const int BUF = 12312334; char Bu

AC日记——「HNOI2017」单旋 LiBreOJ 2018

#2018. 「HNOI2017」单旋 思路: set+线段树: 代码: #include <bits/stdc++.h> using namespace std; #define maxn 100005 #define maxtree maxn<<2 int val[maxtree],tag[maxtree],L[maxtree],R[maxtree],mid[maxtree]; int op[maxn],ki[maxn],bi[maxn],cnt,size,n,ch[maxn]

「随笔」基于当下的思考

马德,说好的技术blog,变成日记本了... 下午的时候莫名其妙的感到很颓废,因为自己的不够强大感到忧虑和危机感十足.现在每每行走在技术的道路上,常觉得如履薄冰,如芒在背. 上大学之前和现在的心态其实差别挺大的,视野的开阔远远不止局限于自己的脚下.不过,这里的「上大学之前」只是一个时间描述词,并不觉得大学是最适合学习的地方,我很失望. 世界上的人无论性别,区域,宗教,兴趣爱好,总可以在互联网上找到志趣相同的人,总是可以不断打破自己的常识与惯性思维.总是有在相同领域比自己更强的人,挺好的. 关于知

「Unity」与iOS、Android平台的整合:3、导出的Android-Studio工程

本文属于「Unity与iOS.Android平台的整合」系列文章之一,转载请注明出处. Unity默认导出的是Android-Eclipse工程,毕竟Eclipse for Android开发在近一两年才开始没落,用户量还是非常巨大的. 个人认为AndroidStudio非常好用,能轻易解决很多Eclipse解决不了或者很难解决的问题. 所以我将Unity导出的Andoid工程分为Eclipse和AndroidStudio两部分. 不过我之后的相关内容都会使用AndroidStudio,希望依然

大数据和「数据挖掘」是何关系?---来自知乎

知乎用户,互联网 244 人赞同 在我读数据挖掘方向研究生的时候:如果要描述数据量非常大,我们用Massive Data(海量数据)如果要描述数据非常多样,我们用Heterogeneous Data(异构数据)如果要描述数据既多样,又量大,我们用Massive Heterogeneous Data(海量异构数据)--如果要申请基金忽悠一笔钱,我们用Big Data(大数据) 编辑于 2014-02-2817 条评论感谢 收藏没有帮助举报作者保留权利 刘知远,NLPer 4 人赞同 我觉得 大数据

开放的智力8:实用「成功学」

可实现的「成功学」 现在我想为这里的年轻人介绍一种可实现的「成功学」.希望这个我自创的理论,可以改变很多人的一生. 当我们评价一个事情值不值得去做.应该花多少精力去做的时候,应该抛弃单一的视角,而是分两个不同的维度来看,一是该事件将给我带来的收益大小(认知.情感.物质.身体方面的收益皆可计入),即「收益值」:二是该收益随时间衰减的速度,我称为「收益半衰期」,半衰期长的事件,对我们的影响会持续得较久较长. 这两个维度正交以后就形成了一个四象限图.我们生活.学习和工作中的所有事情都可以放进这个图里面