其他分享
首页 > 其他分享> > [SCOI2016]萌萌哒

[SCOI2016]萌萌哒

作者:互联网

解题报告

题目传送门:https://www.luogu.com.cn/problem/P3295

解决

题目中给出的限制条件是对应的位置必须填一样的数字,便想到了并查集,然后一波并查集莽上去喜提20分。再然后就不知所措了,只好再去膜一膜大佬,才晓得优化要用倍增跟ST表。

为什么能用倍增呢?

然后题目没说在线询问,所以可以用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\n", ans);
    return 0;
}

标签:include,int,萌萌,合并,fa,区间,SCOI2016,倍增
来源: https://www.cnblogs.com/Zfio/p/12827465.html