• BZOJ3123: [Sdoi2013]森林


    BZOJ3123: [Sdoi2013]森林

    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完成这些操作。

    对于所有的数据,n,m,T<= 8*10^48104 .

    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。之后的操作类似。

    题解Here!

    首先看到第k小,立马想到主席树!

    于是树上第k小就这么愉快的解决了: 对 u,v,LCA(u,v),fa[ LCA(u,v) ] 整体处理——

    int query(int u,int v,int f,int gf,int k,int l,int r){
    	if(l==r)return l;
    	int mid=l+r>>1,t=a[a[u].l].sum+a[a[v].l].sum-a[a[f].l].sum-a[a[gf].l].sum;
    	if(k<=t)return query(a[u].l,a[v].l,a[f].l,a[gf].l,k,l,mid);
    	else return query(a[u].r,a[v].r,a[f].r,a[gf].r,k-t,mid+1,r);
    }
    

    插入主席树时,用父节点的主席树更新此节点的主席树——

    void insert(int k,int l,int r,int &rt,int fa){
    	int mid;
    	rt=++size;a[rt]=a[fa];
    	if(l==k&&k==r){
    		a[rt].sum++;
    		return;
    	}
    	mid=l+r>>1;
    	if(k<=mid)insert(k,l,mid,a[rt].l,a[fa].l);
    	else insert(k,mid+1,r,a[rt].r,a[fa].r);
    	pushup(rt);
    }
    

    但是有个连边操作很烦人。。。

    两个节点连边,就是两棵子树的合并。

    主席树当然可以做到 O(logn) 时间内启发式合并,但是LCA怎么办?又不能启发式。。。

    等等,不能启发式就暴力 dfs 啊!

    时限 2s ,显然可以卡一卡。。。

    由于有修改,LCA当然选倍增。

    于是,一个带倍增处理的暴力 dfs 破空而出——

    void buildtree(int rt,int fa,int ancestry){
    	f[rt][0]=fa;
    	for(int i=1;i<=19;i++)f[rt][i]=f[f[rt][i-1]][i-1];
    	size[ancestry]++;
    	deep[rt]=deep[fa]+1;
    	father[rt]=fa;
    	int x=lower_bound(num+1,num+p+1,val[rt])-num;
    	ST::insert(x,1,p,root[rt],root[fa]);
    	for(int i=head[rt];i;i=a[i].next){
    		int will=a[i].to;
    		if(will==fa)continue;
    		buildtree(will,rt,ancestry);
    	}
    }
    

    然后那个倍增 LCA 就不多说了。

    注意:

    1. 记得离散化!记得离散化!记得离散化!

    2. 那个 testcase 是测试点编号!不是组数!也就是为了方便我们这些蒟蒻打打暴力。。。

    3. 记得优化一下常数,什么 inline 啦,快速读入啦乱搞一波。。。

    附代码:

    #include<iostream>
    #include<algorithm>
    #include<cstdio>
    #define MAXN 80010
    using namespace std;
    int n,m,q,p,c=1;
    int val[MAXN],num[MAXN],root[MAXN];
    int head[MAXN],deep[MAXN],size[MAXN],f[MAXN][20],father[MAXN];
    struct Edge{
    	int next,to;
    }a[MAXN<<2];
    inline int read(){
    	int date=0,w=1;char c=0;
    	while(c<'0'||c>'9'){if(c=='-')w=-1;c=getchar();}
    	while(c>='0'&&c<='9'){date=date*10+c-'0';c=getchar();}
    	return date*w;
    }
    namespace ST{
    	int size=0;
    	struct Segment{
    		int l,r,sum;
    	}a[MAXN*400];
    	inline void pushup(int rt){
    		a[rt].sum=a[a[rt].l].sum+a[a[rt].r].sum;
    	}
    	inline void buildtree(){
    		a[0].l=a[0].r=a[0].sum=root[0]=0;
    	}
    	void insert(int k,int l,int r,int &rt,int fa){
    		int mid;
    		rt=++size;a[rt]=a[fa];
    		if(l==k&&k==r){
    			a[rt].sum++;
    			return;
    		}
    		mid=l+r>>1;
    		if(k<=mid)insert(k,l,mid,a[rt].l,a[fa].l);
    		else insert(k,mid+1,r,a[rt].r,a[fa].r);
    		pushup(rt);
    	}
    	int query(int u,int v,int f,int gf,int k,int l,int r){
    		if(l==r)return l;
    		int mid=l+r>>1,t=a[a[u].l].sum+a[a[v].l].sum-a[a[f].l].sum-a[a[gf].l].sum;
    		if(k<=t)return query(a[u].l,a[v].l,a[f].l,a[gf].l,k,l,mid);
    		else return query(a[u].r,a[v].r,a[f].r,a[gf].r,k-t,mid+1,r);
    	}
    }
    inline int find(int x){return father[x]==x?x:father[x]=find(father[x]);}
    inline void add(int x,int y){
    	a[c].to=y;a[c].next=head[x];head[x]=c++;
    	a[c].to=x;a[c].next=head[y];head[y]=c++;
    }
    void buildtree(int rt,int fa,int ancestry){
    	f[rt][0]=fa;
    	for(int i=1;i<=19;i++)f[rt][i]=f[f[rt][i-1]][i-1];
    	size[ancestry]++;
    	deep[rt]=deep[fa]+1;
    	father[rt]=fa;
    	int x=lower_bound(num+1,num+p+1,val[rt])-num;
    	ST::insert(x,1,p,root[rt],root[fa]);
    	for(int i=head[rt];i;i=a[i].next){
    		int will=a[i].to;
    		if(will==fa)continue;
    		buildtree(will,rt,ancestry);
    	}
    }
    int LCA(int x,int y){
    	if(deep[x]<deep[y])swap(x,y);
    	for(int i=19;i>=0;i--)if(deep[f[x][i]]>=deep[y])x=f[x][i];
    	if(x==y)return x;
    	for(int i=19;i>=0;i--)if(f[x][i]!=f[y][i]){x=f[x][i];y=f[y][i];}
    	return f[x][0];
    }
    void work(){
    	char ch[2];
    	int x,y,k,last=0;
    	while(q--){
    		scanf("%s",ch);x=read()^last;y=read()^last;
    		if(ch[0]=='Q'){
    			k=read()^last;
    			int fa=LCA(x,y);
    			last=num[ST::query(root[x],root[y],root[fa],root[f[fa][0]],k,1,p)];
    			printf("%d
    ",last);
    		}
    		if(ch[0]=='L'){
    			add(x,y);
    			int fx=find(x),fy=find(y);
    			if(size[fx]<size[fy]){
    				swap(x,y);
    				swap(fx,fy);
    			}
    			buildtree(y,x,fx);
    		}
    	}
    }
    void init(){
    	int x,y,cases=read();
    	n=read();m=read();q=read();
    	for(int i=1;i<=n;i++){
    		num[i]=val[i]=read();
    		father[i]=i;
    	}
    	for(int i=1;i<=m;i++){
    		x=read();y=read();
    		add(x,y);
    	}
    	sort(num+1,num+n+1);
    	p=unique(num+1,num+n+1)-num-1;
    	ST::buildtree();
    	for(int i=1;i<=n;i++)if(!deep[i]){buildtree(i,0,i);father[i]=i;}
    }
    int main(){
    	init();
    	work();
    	return 0;
    }
    
  • 相关阅读:
    bootstrap图片上传功能
    Microsoft.EntityFrameworkCore.Sqlite的学习
    问题(待完成):微服务,失败回滚?保持事务的原子性?多步骤调用,如何来实现
    c# 重试机制
    .net core 问题:413 Request Entity Too Large nginx
    Validation
    Tag Helpers in forms in ASP.NET Core
    C/C++中如何获取数组的长度(宏&模板)
    斐波那契数列的实现
    PAT (Basic Level) Practise
  • 原文地址:https://www.cnblogs.com/Yangrui-Blog/p/9085838.html
Copyright © 2020-2023  润新知