zoukankan      html  css  js  c++  java
  • [SCOI2016]萌萌哒

    Luogu P3295

    mrclr两周前做的题让蒟蒻的我现在做?

    第一眼组合计数,如果把数字相同的数位看作一个整体,除了第一位不能为零,剩下的每一位都有$0$~$9$十种。

    设不同的位数为$x$,那么答案即为$9*10$x-1

    给出两段相同的区间,可以把它们看作单独的一位一位对应,用并查集把它们合并。

    复杂度为$O(n^2)$。

    考虑优化。发现修改操作有$n$次,查询只有$1$次。

    有效的修改操作最多只有$n-1$次,所以一定有重复的操作。

    把复杂度平衡一下。合并区间时,直接将大区间合并,最后将合并标记从大到小下传。

    这样,合并的复杂度变小,查询的复杂度变大了。用倍增——也就是二进制拆分的做法,可以让修改、查询的复杂度都变为$O(nlogn)$

    $fa[x][i]$表示从$x$开始,长度为$2^i$的区间的父亲(区间),

    合并时,将一段区间用二进制拆分成若干个区间并合并;

    最后查询时,从最大的区间长度开始枚举,将$[x][i]$的左右两半区间分别与$fa[x][i]$的左右两半区间合并。

    void pushdown() {
        for(int j = 20; j; j--)
            for(int i = 1; i+(1<<j)-1 <= n; i++) {
                int ii = getfa(i,j);
                merge(i,ii,j-1); //左一半 
                merge(i + (1<<(j-1)),ii + (1<<(j-1)),j-1);//右一半
            }
    }

    最后查询并查集个数即可w

    代码如下

    #include<cstdio>
    #include<iostream>
    #define MogeKo qwq
    #define ll long long
    using namespace std;
    const int maxn = 1e5+10;
    const ll mod = 1e9+7;
    
    int n,m,fa[maxn][25];
    int l1,l2,r1,r2;
    ll cnt;
    
    ll qpow(ll a,ll b) {
        ll ans = 1;
        ll base = a;
        while(b) {
            if(b&1) (ans *= base) %= mod;
            (base *= base) %= mod;
            b >>= 1;
        }
        return ans%mod;
    }
    
    void init() {
        for(int i = 1; i <= n; i++)
            for(int j = 0; j <= 20; j++)
                fa[i][j] = i;
    }
    
    int getfa(int x,int k) {
        if(fa[x][k] == x)return x;
        return fa[x][k] = getfa(fa[x][k],k);
    }
    
    void merge(int x,int y,int k) {
        int xx = getfa(x,k);
        int yy = getfa(y,k);
        fa[xx][k] = yy;
    }
    
    void pushdown() {
        for(int j = 20; j; j--)
            for(int i = 1; i+(1<<j)-1 <= n; i++) {
                int ii = getfa(i,j);
                merge(i,ii,j-1);
                merge(i + (1<<(j-1)),ii + (1<<(j-1)),j-1);
            }
    }
    
    int main() {
        scanf("%d%d",&n,&m);
        if(n == 1) {
            printf("10");
            return 0;
        }
        init();
        for(int i = 1; i <= m; i++) {
            scanf("%d%d%d%d",&l1,&r1,&l2,&r2);
            for(int j = 20; j >= 0; j--)
                if(l1+(1<<j)-1 <= r1) {
                    merge(l1,l2,j);
                    l1 += (1<<j);
                    l2 += (1<<j);
                }
        }
        pushdown();
        for(int i = 1; i <= n; i++)
            if(fa[i][0] == i)cnt++;
        printf("%lld",(9*qpow(10,cnt-1))%mod);
        return 0;
    }
    View Code
  • 相关阅读:
    错误日志记录
    解决连接Oracle 11g报ORA-01034和ORA-27101的错误
    windows和linux 下将tomcat注册为服务
    ORA-12170: TNS:Connect timeout occurred
    ubuntu修改时区和时间的方法
    SecureCRT中文显示乱码的解决方法
    修復 “Failed to bring up eth0″ in Ubuntu virtualbox
    HDU 1358 Period
    rcp(插件开发)点击按钮出现 The chosen operation is not enabled 解决办法
    CRM上线之路 走上了CRM实施顾问-第12天上班 -第三周
  • 原文地址:https://www.cnblogs.com/mogeko/p/10699278.html
Copyright © 2011-2022 走看看