[SCOI2016]萌萌哒
Posted zfio
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了[SCOI2016]萌萌哒相关的知识,希望对你有一定的参考价值。
解题报告
题目传送门:https://www.luogu.com.cn/problem/P3295
解决
题目中给出的限制条件是对应的位置必须填一样的数字,便想到了并查集,然后一波并查集莽上去喜提20分。再然后就不知所措了,只好再去膜一膜大佬,才晓得优化要用倍增跟ST表。
为什么能用倍增呢?
- (
众所周知)倍增来源于两个数学等式- 2n=2(n-1)+2(n-1)
- N=(sum_{i=1}^n)( pi × 2i ) (n理论为正无穷)
- 然后...倍增的应用条件是“+”运算满足结合律,或者说直接理解成操作具有可合并性。然后并查集的合并显然是具有合并性的,所以可以倍增之。
倍增果然还是玄学
然后题目没说在线询问,所以可以用ST表。我这连ST表的板子都打不熟呢...
最后的答案即 ans=9×10(N-1),N为并查集的个数,最高位不能为0故1到9九种选择,剩下N-1位每个0到9十种选择。
然后实现就是fa[ i ] [ k ]表示左端点为 i ,区间长度为2^k的区间
的所在集合
的根
的左端点
(套娃)。
简单解释下就是:
初始时所有 fa[ i ] [ k ] = i ,(k∈[0,log(n)]),将区间[5,8]并到区间[1,4]上,就有fa[ 5 ] [ 2 ] = fa[ 1 ] [ 2 ] = 1 (1即为区间[1,4]的左端点)。
总之大概的思路是:用倍增把给出的区间拆成若干个小区间,再合并区间与区间,由于我们最后的目标是点,所以在计算答案时回归到点上。
最后的计算就是将所有对应层的端点合并,做法是将每层和它的上层合并,即[ i ] [ k-1 ] 与 [ Find(i,k) ] [ k-1 ]合并,将[ i+2k-1 ] [ k-1 ]与[ Find(i,k)+2k-1 ] [ k-1 ]合并。
#include <cmath>
#include <cstdio>
#include <iostream>
#include <algorithm>
#define ll long long
#define Dio ios::sync_with_stdio(0)
using namespace std;
const int maxn = 100005, mod = 1000000007;
int n, m, fa[maxn][20], ans;
int find(int x, int k) {
return fa[x][k] == x?x : fa[x][k] = find(fa[x][k], k);
}
void merge(int x, int y, int k) {
x = find(x, k), y = find(y, k);
if(x != y) fa[x][k] = y;
}
int main(){
scanf("%d%d",&n, &m);
for(int i = 1; i <= n; ++i)
for(int k = 0; k <= 20; ++k)
fa[i][k] = i;
for(int i = 1, l1, r1, l2, r2; i <= m; ++i) {
scanf("%d%d%d%d", &l1, &r1, &l2, &r2);
for(int k = 20; ~k; --k)//这里其实k的初始值最好取log(n),但架不住我懒啊
if(l1+(1<<k)-1 <= r1) {
merge(l1, l2, k);
l1 += 1<<k, l2 += 1<<k;
}
}
for(int k = 20; k; --k)//这里也是最好取log(n)的
for(int i = 1; i+(1<<k)-1 <= n; ++i) {
int pos = find(i, k);
merge(i, pos, k-1);
merge(i+(1<<k-1);
pos+(1<<k-1), k-1);
}
for(int i = 1; i <= n; ++i)
if(fa[i][0] == i)
ans = !ans ? 9 : ans * 10ll % mod;
printf("%d
", ans);
return 0;
}
以上是关于[SCOI2016]萌萌哒的主要内容,如果未能解决你的问题,请参考以下文章