zoukankan      html  css  js  c++  java
  • BZOJ2733 [HNOI2012]永无乡 【线段树合并】

    本文版权归ljh2000和博客园共有,欢迎转载,但须保留此声明,并给出原文链接,谢谢合作。

    本文作者:ljh2000
    作者博客:http://www.cnblogs.com/ljh2000-jump/
    转载请注明出处,侵权必究,保留最终解释权!

     

    Description

    永无乡包含 n 座岛,编号从 1 到 n,每座岛都有自己的独一无二的重要度,按照重要度可 以将这 n 座岛排名,名次用 1 到 n 来表示。某些岛之间由巨大的桥连接,通过桥可以从一个岛 到达另一个岛。如果从岛 a 出发经过若干座(含 0 座)桥可以到达岛 b,则称岛 a 和岛 b 是连 通的。现在有两种操作:B x y 表示在岛 x 与岛 y 之间修建一座新桥。Q x k 表示询问当前与岛 x连通的所有岛中第 k 重要的是哪座岛,即所有与岛 x 连通的岛中重要度排名第 k 小的岛是哪 座,请你输出那个岛的编号。 
     

    Input

    输入文件第一行是用空格隔开的两个正整数 n 和 m,分别 表示岛的个数以及一开始存在的桥数。接下来的一行是用空格隔开的 n 个数,依次描述从岛 1 到岛 n 的重要度排名。随后的 m 行每行是用空格隔开的两个正整数 ai 和 bi,表示一开始就存 在一座连接岛 ai 和岛 bi 的桥。后面剩下的部分描述操作,该部分的第一行是一个正整数 q, 表示一共有 q 个操作,接下来的 q 行依次描述每个操作,操作的格式如上所述,以大写字母 Q 或B 开始,后面跟两个不超过 n 的正整数,字母与数字以及两个数字之间用空格隔开。 对于 20%的数据 n≤1000,q≤1000 
     
    对于 100%的数据 n≤100000,m≤n,q≤300000 
     

    Output

    对于每个 Q x k 操作都要依次输出一行,其中包含一个整数,表 示所询问岛屿的编号。如果该岛屿不存在,则输出-1。 
     

    Sample Input

    5 1
    4 3 2 5 1
    1 2
    7
    Q 3 2
    Q 2 1
    B 2 3
    B 1 5
    Q 2 1
    Q 2 4
    Q 2 3

    Sample Output

    -1
    2
    5
    1
    2

    正解:线段树合并

    解题报告:

      我这蒟蒻以前一直没写过线段树合并QAQ

      线段树合并支持一类值域统计的问题,假设我对于每个结点都维护一棵关于值域的线段树(显然没有值的结点没必要建),跟主席树有一点像…

      然后用并查集维护联通情况,每次需要将两个结点所代表的两棵线段树进行合并,因为两棵线段树值域相同,所以可以直接对线段树进行合并。

      合并方法就是,每次访问一个结点,如果其中一个为空则返回另一个结点,否则就把一个合到另一个上去,并且sum相加。

      这样一来可以发现,每个联通块实际上就是共用了一棵线段树。

      总复杂度:$O(nlogn)$

    //It is made by ljh2000
    #include <iostream>
    #include <cstdlib>
    #include <cstring>
    #include <cstdio>
    #include <cmath>
    #include <algorithm>
    using namespace std;
    typedef long long LL;
    const int MAXN = 100011;
    int n,m,q,w[MAXN],cnt,root[MAXN];
    int father[MAXN],pos[MAXN];
    char ch[12];
    struct node{ int ls,rs,sum; }a[MAXN*20];
    inline int find(int x){ if(father[x]!=x) father[x]=find(father[x]); return father[x]; }
    inline int getint(){
        int w=0,q=0; char c=getchar(); while((c<'0'||c>'9') && c!='-') c=getchar();
        if(c=='-') q=1,c=getchar(); while (c>='0'&&c<='9') w=w*10+c-'0',c=getchar(); return q?-w:w;
    }
    
    inline void insert(int &k,int l,int r,int val){
    	if(!k) k=++cnt; if(l==r) { a[k].sum++; return ; } int mid=(l+r)>>1; 
    	if(val<=mid) insert(a[k].ls,l,mid,val);
    	else insert(a[k].rs,mid+1,r,val);
    	a[k].sum=a[a[k].ls].sum+a[a[k].rs].sum;
    }
    
    inline int query(int k,int l,int r,int val){
    	if(l==r) return l; int mid=(l+r)>>1;
    	if(val<=a[a[k].ls].sum) return query(a[k].ls,l,mid,val);
    	else return query(a[k].rs,mid+1,r,val-a[a[k].ls].sum);
    }
    
    inline int merge(int x,int y){
    	if(!x) return y;
    	if(!y) return x;
    	a[y].ls=merge(a[x].ls,a[y].ls);
    	a[y].rs=merge(a[x].rs,a[y].rs);
    	a[y].sum=a[a[y].ls].sum+a[a[y].rs].sum;
    	return y;
    }
    
    inline void work(){
    	n=getint(); m=getint(); int x,y,r1,r2;
    	for(int i=1;i<=n;i++) father[i]=i;
    	for(int i=1;i<=n;i++) w[i]=getint();
    	for(int i=1;i<=m;i++) {
    		x=getint(); y=getint();
    		r1=find(x); r2=find(y);
    		father[r1]=r2;
    	}
    	for(int i=1;i<=n;i++) {
    		insert(root[find(i)],1,n,w[i]);
    		pos[w[i]]=i;
    	}
    	q=getint();
    	while(q--) {
    		scanf("%s",ch);
    		if(ch[0]=='Q') {
    			x=getint(); y=getint();
    			if(a[root[find(x)]].sum<y) { printf("-1
    "); continue; }
    			r1=query(root[find(x)],1,n,y);//权值为r1的是第k大的,倒推回位置
    			printf("%d
    ",pos[r1]);
    		}
    		else {
    			x=getint(); y=getint(); x=find(x); y=find(y);
    			if(x!=y) {
    				father[x]=y;
    				root[y]=merge(root[x],root[y]);
    			}
    		}
    	}
    }
    
    int main()
    {
        work();
        return 0;
    }
    

      

  • 相关阅读:
    topcoder srm 681 div1
    topcoder srm 683 div1
    topcoder srm 684 div1
    topcoder srm 715 div1
    topcoder srm 685 div1
    topcoder srm 687 div1
    topcoder srm 688 div1
    topcoder srm 689 div1
    topcoder srm 686 div1
    topcoder srm 690 div1 -3
  • 原文地址:https://www.cnblogs.com/ljh2000-jump/p/6287640.html
Copyright © 2011-2022 走看看