BZOJ 4569 [Scoi2016]萌萌哒

来源:互联网 发布:模拟软件下载 编辑:程序博客网 时间:2024/05/21 14:43

ST表(倍增)+并查集

一个直观的想法是把[l1,r1],[l2,r2]对应位置的元素并起来。若最后有cnt个集合,则答案就是910cnt1,复杂度O(nm),考虑优化它。

实际上我们是在进行区间操作,那我们考虑直接对区间进行维护。用ST表,记id[i][j]表示[i,i+2j1]这一段节点,记一个并查集表示并查集内所有元素表示的区间完全相同

两段区间合并,实际上可以转化为两个ST表上区间的合并,用并查集维护这个过程。合并即可降为每次O(α(n)),α(n)是n个元素的并查集的复杂度。

考虑标记下传,若两个区间[i,i+2j1],[k,k+2j1]拥有相同父集合,充要条件是这两个区间要完全相同。此时把这种信息传给左右儿子(jj1)即可。最终统计j=0的叶节点情况即可。

写下这篇文章时访问量为9972,什么时候破万呢???^v^

#include<cstdio>#define N 300005#define H 18#define MOD 1000000007using namespace std;namespace runzhe2000{    int f[N*H], log[N], num, id[N][H], beg[N*H]; long long ans=1;    bool vis[N*H];    void add(){ans==1?ans*=9:ans*=10; ans %= MOD;}    int find(int x){return f[x] == x ? x : f[x] = find(f[x]); }    void unio(int x, int y)    {        f[find(x)] = find(y);    }    void main()    {        int n, m;        scanf("%d%d",&n,&m);        if(n == 1){puts("10"); return;}        for(int i = 1; i <= n; i++)            for(int j = H-1; j >= 0; j--)            {                id[i][j] = ++num;                beg[num] = i;                f[num] = num;            }        for(int i = 2; i <= n; i++) log[i] = log[i>>1] + 1;        for(int i = 1; i <= m; i++)        {            int l1, r1, l2, r2;            scanf("%d%d%d%d",&l1,&r1,&l2,&r2);            int len = r1 - l1 + 1;            unio(id[l1][log[len]], id[l2][log[len]]);            unio(id[r1-(1<<log[len])+1][log[len]], id[r2-(1<<log[len])+1][log[len]]);        }        for(int j = H-1; j; j--)            for(int i = 1; i <= n; i++)            {                int f = find(id[i][j]);                unio(id[beg[id[i][j]]][j-1], id[beg[f]][j-1]);                unio(id[beg[id[i][j]]+(1<<(j-1))][j-1], id[beg[f]+(1<<(j-1))][j-1]);            }        for(int i = 1; i <= n; i++)        {            int fi = find(id[i][0]);            if(!vis[fi]) add(), vis[fi] = 1;        }        printf("%lld\n",ans);    }}int main(){    runzhe2000::main();}
0 0