• SDOI2013 森林


    codevs 951 森林

    http://codevs.cn/problem/1951/

    2013年省队选拔赛

     时间限制: 4 s
     空间限制: 256000 KB
     
    题目描述 Description

        小 Z有一片森林, 含有 N个节点, 每个节点上都有一个非负整数作为权值。初始的时候,森林中有 M 条边。 
        小 Z希望执行T 个操作,操作有两类: 
        1、Q x y k 查询点 x 到点 y路径上所有的权值中,第k小的权值是多少。此操作保证点x 和点 y连通,同时这两个节点的路径上至少有 k个点。 
        2、L x y 在点x 和点 y之间连接一条边。保证完成此操作后,仍然是一片森林。 
        为了体现程序的在线性,我们把输入数据进行了加密。设 lastans 为程序上一次输出的结果,初始的时候 lastans 为0。 
        对于一个输入的操作 Q x y k,其真实操作为 Q x^lastans y^lastans k^lastans。 
        对于一个输入的操作 L x y,其真实操作为 L x^lastans y^lastans。 
        其中^运算符表示异或,等价于pascal 中的 xor运算符。 
        请写一个程序来帮助小 Z完成这些操作。

    输入描述 Input Description

        第一行包含一个正整数 testcase,表示当前测试数据的测试点编号。保证 1≤testcase≤20。 
        第二行包含三个整数 N,M,T,分别表示节点数、初始边数、操作数。 
        第三行包含N个非负整数表示N个节点上的权值。 
        接下来 M 行,每行包含两个整数 x 和 y,表示初始的时候,点 x 和点 y 之间有一条无向边。 
        接下来 T行,每行描述一个操作,格式为“Q x y k”或者“L x y”,其含义见题目描述部分。

    输出描述 Output Description

    对于每一个第一类操作,输出一个非负整数表示答案。 

    样例输入 Sample Input


    8 4 8 
    1 1 2 2 3 3 4 4 
    4 7 
    1 8 
    2 4 
    2 1 
    Q 8 7 3 
    Q 3 5 1
    Q 10 0 0
    L 5 4 
    L 3 2 
    L 0 7 
    Q 9 2 5 
    Q 6 1 6

    样例输出 Sample Output





    2

    数据范围及提示 Data Size & Hint

    【样例说明】 
    解密后的操作为 
    Q 8 7 3 
    Q 1 7 3 
    Q 8 2 2 
    L 4 5 
    L 2 3 
    L 1 6 
    Q 8 3 4 
    Q 2 5 2 

        对于第一个操作 Q 8 7 3,此时lastans=0,所以真实操作为 Q 8^0 7^0 3^0,也即Q 8 7 3。点8到点 7的路径上一共有5个点,其权值为 4 1 1 2 4。这些权值中,第三小的为2,输出 2,lastans 变为2。 
        对于第二个操作 Q 3 5 1,此时lastans=2,所以真实操作为 Q 3^2 5^2 1^2,也即Q 1 7 3。点1到点 7的路径上一共有4个点,其权值为 1 1 2 4。这些权值中,第三小的为2,输出 2,lastans 变为2。
        之后的操作类似。 


    【数据范围】

    测试点编号 N,M,T的上限 L操作 Q操作 形态
    1 20 N/A N/A N/A
    2 200 N/A N/A N/A
    3 4*104 无L操作 N/A
    4 4*104 无L操作 N/A
    5 8*104 无L操作 N/A
    6 8*104 无L操作 N/A
    7 8*104 无L操作 保证k=1 N/A
    8 8*104 无L操作 保证k=1 N/A
    9 8*104 无L操作 保证k=1 N/A
    10 4*104 N/A 保证k=1 N/A
    11 4*104 N/A 保证k=1 N/A
    12 8*104 N/A 保证k=1 N/A
    13 8*104 N/A 保证k=1 N/A
    14 4*104 无L操作 N/A N/A
    15 4*104 无L操作 N/A N/A
    16 8*104 无L操作 N/A N/A
    17 8*104 无L操作 N/A N/A
    18 4*104 N/A N/A N/A
    19 8*104 N/A N/A N/A
    20 8*104 N/A N/A N/A 

    N/A表示没有特殊性

    主席树+启发式合并+并查集
    以点权为区间,点的出现或合并顺序为下标建立主席树
    合并的时候,选出小的一棵树合并到大的上
    并查集的作用就是判断树的大小
    注意并查集内的父子关系与合并的对应

    在树上,以根为基准建立线段树

    u,v间增加的点数=siz[u]+siz[v]-siz[LCA(u,v)]-siz[father[LCA(u,v)]]

    查询的时候需要找lca,如何找?

    仍然是采用倍增的思想

    先让深度深的点跳到与另一点深度相同的位置,然后两个点一起往上跳

    为什么不用dfs序判断?

    因为本题还需要动态维护lca

    合并两棵树不像 HNOI2012 永无乡 http://www.cnblogs.com/TheRoadToTheGold/p/6520714.html

    那道题是与某个节点像联通的第k值

    而这道题指明路径,就要维护父子关系不能错乱

    所以不记录dfs序,记录深度

    合并时,

    假设y与x之间连一条边,y所在树大小<x

    很明显要把y所在树合并到x上

    将y树全部打散,以y为根节点重组y树

    也就是y点挂到x点下面

    原来的y树中y的子节点挂到y下面,以此类推

    可以用bfs也可以用dfs

    注:本代码可以在codevs 、bzoj 上 AC,但在洛谷上 TLE1个点,MLE1个点 

    #include<cstdio>
    #include<algorithm>
    #define N 160001
    using namespace std;
    int t,n,m,q,tot,edge_tot,tree_tot,cnt;
    int key[N],hashh[N],deep[N],F[N],siz[N*75],siz_b[N],lc[N*75],rc[N*75],ancestor[N][26],root[N];
    int front[N],nextt[N*2],to[N*2];
    int read()
    {
        int x=0;char c=getchar();
        while(c<'0'||c>'9') c=getchar();
        while(c>='0'&&c<='9') {x=x*10+c-'0';c=getchar();}
        return x;
    }
    int find(int x) {return x==F[x] ? x:F[x]=find(F[x]);}
    void discrete()
    {
        sort(key+1,key+n+1);
        tot=unique(key+1,key+n+1)-(key+1);
    }
    void add(int u,int v)
    {
        to[++edge_tot]=v;nextt[edge_tot]=front[u];front[u]=edge_tot;
    }
    void insert(int pre,int &x,int l,int r,int w)
    {
        siz[x=++tree_tot]=siz[pre]+1;
        if(l==r) return;
        int mid=l+r>>1;
        if(w<=mid)
        {
            rc[x]=rc[pre];
            insert(lc[pre],lc[x],l,mid,w);
        }
        else
        {
            lc[x]=lc[pre];
            insert(rc[pre],rc[x],mid+1,r,w);
        }
    }
    void dfs(int now,int f)
    {
        ancestor[now][0]=f;siz_b[now]=1;
        int p=lower_bound(key+1,key+tot+1,hashh[now])-key;
        insert(root[f],root[now],1,tot,p);
        for(int i=front[now];i;i=nextt[i])
        {
            if(to[i]==f) continue;
            deep[to[i]]=deep[now]+1;
            dfs(to[i],now);
            siz_b[now]+=siz_b[to[i]];
        }
    }
    int get_same(int x,int y)
    {
        for(int i=0;i<=20;i++)
         if(y&(1<<i)) x=ancestor[x][i];
        return x;  
    }
    int get_lca(int x,int y)
    {
        if(deep[x]<deep[y]) swap(x,y);
        x=get_same(x,deep[x]-deep[y]);
        if(x==y) return x;
        for(int i=20;i>=0;i--)
         if(ancestor[x][i]!=ancestor[y][i]) 
          {
               x=ancestor[x][i];y=ancestor[y][i];
          }
        return ancestor[x][0];
    }
    int query(int x,int y,int lca,int flca,int l,int r,int k)
    {
        if(l==r) return l;
        int mid=l+r>>1,tmp=siz[lc[x]]+siz[lc[y]]-siz[lc[lca]]-siz[lc[flca]];
        if(k<=tmp) return query(lc[x],lc[y],lc[lca],lc[flca],l,mid,k);
        else return query(rc[x],rc[y],rc[lca],rc[flca],mid+1,r,k-tmp);
    }
    void unionn(int x,int y)
    {
        ancestor[y][0]=x;deep[y]=deep[x]+1;
        int p=lower_bound(key+1,key+tot+1,hashh[y])-key;
        insert(root[x],root[y],1,tot,p);
        for(int i=1;i<=20;i++) ancestor[y][i]=ancestor[ancestor[y][i-1]][i-1];
        for(int i=front[y];i;i=nextt[i])
        {
            if(to[i]==x) continue;
            unionn(y,to[i]); 
        }
    }
    void get_ancestor()
    {
        for(int i=1;i<=20;i++)
         for(int j=1;j<=n;j++)
          ancestor[j][i]=ancestor[ancestor[j][i-1]][i-1];
    }
    int main()
    {
        //scanf("%d%d%d%d",&t,&n,&m,&q);
        t=read();n=read();m=read();q=read();    
        for(int i=1;i<=n;i++)
        {
            //scanf("%d",&key[i]);
            key[i]=read();
            F[i]=i;hashh[i]=key[i];
        }
        int x,y,z,last=0;
        char c[2];
        for(int i=1;i<=m;i++)
        {
            //scanf("%d%d",&x,&y);
            x=read();y=read();
            int r1=find(x),r2=find(y);
            F[r1]=r2;
            add(y,x);add(x,y);
        }
        discrete();
        for(int i=1;i<=n;i++) if(!ancestor[i][0]) dfs(i,0);
        get_ancestor();
        for(int i=1;i<=q;i++)
        {
            scanf("%s",c);
            if(c[0]=='Q')
            {
                //scanf("%d%d%d",&x,&y,&z);
                x=read();y=read();z=read();
                x^=last;y^=last;z^=last;
                int lca=get_lca(x,y);
                last=query(root[x],root[y],root[lca],root[ancestor[lca][0]],1,tot,z);
                last=key[last];
                printf("%d
    ",last);
            }
            else
            {
                //scanf("%d%d",&x,&y);
                x=read();y=read();
                x^=last;y^=last;
                int r1=find(x),r2=find(y);
                if(siz_b[r1]<siz_b[r2]) swap(r1,r2),swap(x,y);
                F[r2]=r1;siz_b[r1]+=siz_b[r2];
                add(x,y);add(y,x);
                unionn(x,y);
            }
        }
    }
  • 相关阅读:
    js-事件总结
    iquery-个人总结
    CSS3-页面样式
    野生码农狂奔中的2014年度年终总结
    算不算被虚度的这几年
    在Ubuntu 12.4 下安装 nginx, MySQL, PHP
    使用WP8最新的AudioVideoCaptureDevice类制作录像应用
    WP8 MediaElement 实现循环播放
    WPF(WP7、WP8)多个Listbox嵌套时滚动问题的解决
    Weibo SDK WP版本回调参数没有uid的解决方法
  • 原文地址:https://www.cnblogs.com/TheRoadToTheGold/p/6524904.html
Copyright © 2020-2023  润新知