码迷,mamicode.com
首页 > 其他好文 > 详细

【题解】SCOI2006萌萌哒

时间:2018-09-15 23:17:42      阅读:196      评论:0      收藏:0      [点我收藏+]

标签:特殊性   线段   its   turn   init   ace   getc   std   而且   

  看到这题,首先想到\(n^{2}\)的暴力,就是用并查集暴力合并两个相等的点。但由于这样会导致反复地访问同一个操作,显然是不能够的。于是我们可以联想这题的特殊性质,就是互相连变的点都是一段一段的区间。然后很自然地联想到线段树分解优化,坚定地想了一个半小时还多,然后很自然地挂了。天知道我是怎么把一个暴力的复杂度给生生算出 \(nlog^{2}m\) 的复杂度来的……(⊙﹏⊙)

  线段树的区间分割并不是很灵活,而且完全没有改变暴力的本质。于是灰溜溜的去看题解,倍增?恍然大悟一般。是啊,分解区间我们还有倍增呀~我们可以用 \(f[i][j]\) 表示 \(i\) 与向后的 \(2^{j}\) 个点,我们就可以\(O(1))\)地完成区间的合并了。最后,由于如果 \(f[i][j]\) 与 \(f[k][j]\) 是相等的,那么他们下面的所有倍增区间也都是相等的。我们类似 push_down 操作,让儿子继承一下父亲的集合关系即可。受教啦~

#include <bits/stdc++.h>
using namespace std;
#define maxn 100500
#define mod 1000000007
#define LL long long
int n, m, cnt, fa[maxn * 20];
int lc[maxn * 20], rc[maxn * 20];
int bit[20], Log[maxn], f[maxn][20];

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

void init()
{
    bit[0] = 1; for(int i = 1; i < 20; i ++) bit[i] = bit[i - 1] << 1;
    Log[0] = -1; for(int i = 1; i <= n; i ++) Log[i] = Log[i >> 1] + 1;
    for(int j = 0; bit[j] <= n; j ++)    
        for(int i = 1; i + bit[j] - 1 <= n; i ++)
        {
            f[i][j] = ++ cnt;
            if(j)
            {
                lc[cnt] = f[i][j - 1];
                rc[cnt] = f[i + bit[j - 1]][j - 1];
            }
        }
    for(int i = 1; i <= cnt; i ++) fa[i] = i;
}

int find(int x) { return ((fa[x] == x) ? x : fa[x] = find(fa[x])); }
void merge(int x, int y)
{
    x = find(x), y = find(y);
    if(x != y) fa[x] = y;
}

int main()
{
    n = read(), m = read();
    init();
    for(int i = 1; i <= m; i ++)
    {
        int l1 = read(), r1 = read(), l2 = read(), r2 = read();
        int k = Log[r1 - l1 + 1];
        merge(f[l1][k], f[l2][k]);
        merge(f[r1 - bit[k] + 1][k], f[r2 - bit[k] + 1][k]);
    }
    for(int i = cnt, tem; i > n; i --)
        if((tem = find(i)) != i)
        {
            merge(lc[i], lc[tem]);
            merge(rc[i], rc[tem]);
        }
    int ans = 0, base = 9;
    for(int i = 1; i <= n; i ++) ans += (find(i) == i);
    for(int i = 1; i < ans; i ++) base = ((LL) base * 10LL) % mod;
    printf("%d\n", base);
    return 0;
}

 

【题解】SCOI2006萌萌哒

标签:特殊性   线段   its   turn   init   ace   getc   std   而且   

原文地址:https://www.cnblogs.com/twilight-sx/p/9652638.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!