zoukankan      html  css  js  c++  java
  • BZOJ 3123: [Sdoi2013]森林

    3123: [Sdoi2013]森林

    Time Limit: 20 Sec  Memory Limit: 512 MB
    Submit: 2914  Solved: 853
    [Submit][Status][Discuss]

    Description

    Input

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

    Output

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

    Sample Input

    1
    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
    2
    1
    4
    2

    HINT



    对于第一个操作 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。之后的操作类似。 

     

    Source

    分析:

    如果没有L操作就很好办了,直接维护每个节点的到根节点的路径上的权值线段树,然后加加减减就好了...

    现在有L操作就是说我们要合并两棵树以及更新每个节点的信息,那么因为要更新信息,所以一定是要遍历一遍某课子树,所以我们采用启发式合并主席树,每一次重新dfs小的那棵树重建每个节点的权值权值线段树...

    时间复杂度$O(Nlog^{2}N)$...然而我的比别人的跑的都慢...加了快读才勉强卡过去...

    代码:

    #include<algorithm>
    #include<iostream>
    #include<cstring>
    #include<cstdio>
    //by NeighThorn
    using namespace std;
    
    const int maxn=200000+5,maxm=40000000+5;
    
    int n,m,t,T,cas,cnt,len,tot,w[maxn],mp[maxn],ls[maxm],rs[maxm],hd[maxn],fa[maxn][25],to[maxn],nxt[maxn],sum[maxm],dep[maxn],siz[maxn],vis[maxn],root[maxn],father[maxn];
    
    inline int read(void){
    	char ch=getchar();int x=0,f=1;
    	while(!(ch>='0'&&ch<='9')){
    		if(ch=='-')
    			f=-1;
    		ch=getchar();
    	}
    	while(ch>='0'&&ch<='9')
    		x=x*10+ch-'0',ch=getchar();
    	return f*x;
    }
    
    inline int Find(int x){
    	return father[x]==x?x:father[x]=Find(father[x]);
    }
    
    inline int find(int x){
    	int _=1,__=len,___;
    	while(_<=__){
    		int ____=(_+__)>>1;
    		if(mp[____]>=x)
    			___=____,__=____-1;
    		else
    			_=____+1;
    	}
    	return ___;
    }
    
    inline void add(int x,int y){
    	to[cnt]=y;nxt[cnt]=hd[x];hd[x]=cnt++;
    }
    
    inline void change(int l,int r,int x,int &y,int val){
        y=++tot,sum[y]=sum[x]+1;
        if(l==r)
            return;
        int mid=(l+r)>>1;ls[y]=ls[x],rs[y]=rs[x];
        if(val<=mid)
            change(l,mid,ls[x],ls[y],val);
        else
            change(mid+1,r,rs[x],rs[y],val);
    }
    
    inline int LCA(int x,int y){
        if(dep[x]<dep[y])
            swap(x,y);
        int d=dep[x]-dep[y];
        for(int i=0;i<=20;i++)
            if((d>>i)&1)
                x=fa[x][i];
        if(x==y)
            return x;
        for(int i=20;i>=0;i--)
            if(fa[x][i]!=fa[y][i])
                x=fa[x][i],y=fa[y][i];
        return fa[x][0];
    }
    
    inline void dfs(int rt){
    	vis[rt]=T;change(1,len,fa[rt][0]==rt?root[0]:root[fa[rt][0]],root[rt],find(w[rt]));
    	for(int j=1;j<=20;j++)
    		fa[rt][j]=fa[fa[rt][j-1]][j-1];
    	for(int i=hd[rt];i!=-1;i=nxt[i])
    		if(vis[to[i]]!=T)
    			fa[to[i]][0]=rt,dep[to[i]]=dep[rt]+1,dfs(to[i]);
    }
    
    inline int query(int l,int r,int x,int y,int lca,int flca,int num){
        if(l==r)
            return l;
        int mid=(l+r)>>1;
        if(sum[ls[x]]+sum[ls[y]]-sum[ls[lca]]-sum[ls[flca]]>=num)
            return query(l,mid,ls[x],ls[y],ls[lca],ls[flca],num);
        else
            return query(mid+1,r,rs[x],rs[y],rs[lca],rs[flca],num-(sum[ls[x]]+sum[ls[y]]-sum[ls[lca]]-sum[ls[flca]]));
    }
    
    signed main(void){
    	cas=read();
    	cnt=0;len=0;tot=0;T=1;
    	memset(hd,-1,sizeof(hd));
    	n=read();m=read();t=read();
    	for(int i=1;i<=n;i++)
    		w[i]=read(),mp[i]=w[i];
    	sort(mp+1,mp+n+1);len=unique(mp+1,mp+n+1)-mp-1;
    	for(int i=1,x,y;i<=m;i++)
    		x=read(),y=read(),add(x,y),add(y,x);
    	for(int i=1;i<=n;i++)
    		father[i]=i,siz[i]=1;
    	for(int i=1;i<=n;i++)
    		if(vis[i]!=T)
    			dep[i]=0,fa[i][0]=i,dfs(i);
    	T++;char opt[3];int ans=0;
    	for(int i=1,s,x,y;i<=t;i++){
    		scanf("%s",opt);
    		if(opt[0]=='Q'){
    			x=read(),y=read(),s=read();x=x^ans,y=y^ans,s=s^ans;
    			int lca=LCA(x,y);
    			printf("%d
    ",ans=mp[query(1,len,root[x],root[y],root[lca],fa[lca][0]==lca?root[0]:root[fa[lca][0]],s)]);
    		}
    		else{
    			x=read(),y=read();x=x^ans,y=y^ans;
    			int fx=Find(x),fy=Find(y);
    			if(siz[fx]>siz[fy])
    				swap(x,y),swap(fx,fy);
    			fa[x][0]=y,dep[x]=dep[y]+1,dfs(x),add(x,y),add(y,x);father[fx]=fy,siz[fy]+=siz[fx];T++;
    		}
    	}
    	return 0;
    }
    

      


    By NeighThorn

  • 相关阅读:
    if..endif 语法
    WordPress程序流程分析
    php锁表
    jQuery入门必须掌握的一些API
    集合栈
    回文链表
    链式A+B
    链表分割
    访问单个节点的删除
    链表中倒数第k个结点
  • 原文地址:https://www.cnblogs.com/neighthorn/p/6396108.html
Copyright © 2011-2022 走看看