列表

详情


NC20577. [SDOI2013]森林

描述

小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。

请写一个程序來帮助小Z完成这些操作。
对于所有的数据,n,m,T<= 8∗10^4.

输入描述

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

输出描述

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

示例1

输入:

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

输出:

2
2
1
4
2

原站题解

上次编辑到这里,代码来自缓存 点击恢复默认模板

C++14(g++5.4) 解法, 执行用时: 480ms, 内存消耗: 111636K, 提交时间: 2020-09-28 19:48:52

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=8e4+10;
int fa[N][21],dep[N],head[N],cnt,tot,a[N],b[N],T[N],m,ffa[N],n,siz[N];
bool vis[N];
struct Tree{
    int l,r,sum;
}tree[N*500];
struct edge{
    int next,to;
}e[N<<1];
void add(int u,int v){
    e[++cnt].next=head[u];
    e[cnt].to=v;
    head[u]=cnt;
}
int LCA(int u,int v){
    if(dep[u]<dep[v])swap(u,v);
    for(int i=20;i>=0;i--)
        if(dep[fa[u][i]]>=dep[v])u=fa[u][i];
    if(u==v)return u;
    for(int i=20;i>=0;i--)
        if(fa[u][i]!=fa[v][i])
            u=fa[u][i],v=fa[v][i];
    return fa[u][0];
}
int build(int l,int r){
    int id=++tot;tree[id].sum=0;
    if(l==r)return id;
    int mid=l+r>>1;
    tree[id].l=build(l,mid);
    tree[id].r=build(mid+1,r);
    return id;
}
int update(int l,int r,int rt,int x){
    int id=++tot;tree[id]=tree[rt];
    if(l==r){tree[id].sum++;return id;}
    int mid=l+r>>1;
    if(x<=mid)tree[id].l=update(l,mid,tree[rt].l,x);
    else tree[id].r=update(mid+1,r,tree[rt].r,x);
    tree[id].sum=tree[tree[id].l].sum+tree[tree[id].r].sum;
    return id;
}
int query(int l,int r,int k,int u,int v,int lca1,int lca2){
    if(l==r)return b[l];
    int sum=tree[tree[u].l].sum+tree[tree[v].l].sum-tree[tree[lca1].l].sum-tree[tree[lca2].l].sum;
    int mid=l+r>>1;
    if(k<=sum)return query(l,mid,k,tree[u].l,tree[v].l,tree[lca1].l,tree[lca2].l);
    else return query(mid+1,r,k-sum,tree[u].r,tree[v].r,tree[lca1].r,tree[lca2].r);
}
void dfs(int u,int f){
    dep[u]=dep[f]+1;fa[u][0]=f;vis[u]=true;
    T[u]=update(1,m,T[f],a[u]);
    for(int i=1;i<=20;i++)fa[u][i]=fa[fa[u][i-1]][i-1];
    for(int i=head[u];i;i=e[i].next){
        int v=e[i].to;if(v==f)continue;dfs(v,u);
    }
}
int _find(int x){return x==ffa[x]?x:ffa[x]=_find(ffa[x]);}
void _union(int &u,int &v){
    int x=_find(u),y=_find(v);
    if(siz[x]<siz[y])swap(x,y),swap(u,v);
    siz[x]+=siz[y];ffa[y]=x;
}
int main(){
    int t;scanf("%d",&t);scanf("%d",&n);int q1,q2;scanf("%d %d",&q1,&q2);
    for(int i=1;i<=n;i++)scanf("%d",&a[i]),b[i]=a[i];
    sort(b+1,b+n+1);m=unique(b+1,b+n+1)-b-1;
    for(int i=1;i<=n;i++)a[i]=lower_bound(b+1,b+m+1,a[i])-b,ffa[i]=i,siz[i]=1;
    for(int i=1;i<=q1;i++){
        int x,y;scanf("%d %d",&x,&y);_union(x,y);add(x,y);add(y,x);
    }
    T[0]=build(1,m);
    for(int i=1;i<=n;i++)
        if(!vis[i])
            dfs(_find(i),0);
    int ans=0;
    while(q2--){
        char op;scanf(" %c",&op);
        if(op=='Q'){
            int u,v,k;scanf("%d %d %d",&u,&v,&k);u^=ans,v^=ans,k^=ans;int lca=LCA(u,v);printf("%d\n",ans=query(1,m,k,T[u],T[v],T[lca],T[fa[lca][0]]));
        }else {
            int u,v;scanf("%d %d",&u,&v);u^=ans,v^=ans;_union(u,v);add(v,u);add(u,v);dfs(v,u);
        }
    }
}

C++ 解法, 执行用时: 550ms, 内存消耗: 108816K, 提交时间: 2022-01-27 14:53:02

#pragma GCC optimize("O3")
#pragma GCC optimize("unroll-loops")
#include<iostream>
#include<algorithm>
#include<vector>
#include<cstring>

using namespace std;
const int maxn=1e5+5;

char op;
int a[maxn],b[maxn],n,m,q,mx,u,v,w,ans;
int dep[maxn],fa[maxn][18],lg[maxn],ft[maxn],sz[maxn];
int ls[maxn<<7],rs[maxn<<7],sum[maxn<<7],rt[maxn],now;
vector<int>ed[maxn];

int find(int x)
{
	return ft[x]==x?x:ft[x]=find(ft[x]);
}

void update(int u,int&v,int l,int r,int pos)
{
	v=++now,ls[v]=ls[u],rs[v]=rs[u],sum[v]=sum[u]+1;
	if(l==r)return;
	int mid=l+r>>1;
	if(pos<=mid)update(ls[u],ls[v],l,mid,pos);
	else update(rs[u],rs[v],mid+1,r,pos);
	return;
}

int query(int u1,int u2,int v1,int v2,int l,int r,int k)
{
	if(l==r)return b[l];
	int mid=l+r>>1,num=sum[ls[v1]]+sum[ls[v2]]-sum[ls[u1]]-sum[ls[u2]];
	if(num>=k)return query(ls[u1],ls[u2],ls[v1],ls[v2],l,mid,k);
	else return query(rs[u1],rs[u2],rs[v1],rs[v2],mid+1,r,k-num);
}

int LCA(int x,int y)
{
	if(dep[x]<dep[y])swap(x,y);
	while(dep[x]>dep[y])
	{
		x=fa[x][lg[dep[x]-dep[y]]-1];
	}
	if(x==y)return x;
	for(int i=17;i>=0;i--)
	{
		if(fa[x][i]!=fa[y][i])
		{
			x=fa[x][i],y=fa[y][i];
		}
	}
	return fa[x][0];
}

void dfs(int x,int f,int t)
{
	dep[x]=dep[f]+1,fa[x][0]=f,sz[t]++,ft[x]=t;
	update(rt[f],rt[x],1,mx,a[x]);
	for(int i=1;i<=17;i++)
	{
		fa[x][i]=fa[fa[x][i-1]][i-1];	
	}	
	for(auto y:ed[x])
	{
		if(y!=f)dfs(y,x,t);
	}
	return;
}

int main()
{
	ios::sync_with_stdio(false);
	cin.tie(0); cout.tie(0);
	int T; cin>>T;
	for(int i=1;i<=1e5;i++)lg[i]=lg[i-1]+(!(i&i-1));
	cin>>n>>m>>q;
	for(int i=1;i<=n;i++)cin>>a[i],b[i]=a[i],ft[i]=i;
	sort(b+1,b+1+n); mx=unique(b+1,b+1+n)-b-1;
	for(int i=1;i<=n;i++)a[i]=lower_bound(b+1,b+1+mx,a[i])-b;
	for(int i=1;i<=m;i++)
	{
		cin>>u>>v;
		ed[u].push_back(v);
		ed[v].push_back(u);  
	}
	for(int i=1;i<=n;i++)if(ft[i]==i)dfs(i,0,i);
	for(int i=1;i<=q;i++)
	{
		cin>>op;
		if(op=='Q')
		{
			cin>>u>>v>>w;
			u^=ans,v^=ans,w^=ans;
			int lca=LCA(u,v);
			ans=query(rt[lca],rt[fa[lca][0]],rt[u],rt[v],1,mx,w);
			cout<<ans<<"\n";
		}
		else
		{
			cin>>u>>v;
			u^=ans,v^=ans;
			int x=find(u),y=find(v);
			if(sz[x]<sz[y])swap(x,y),swap(u,v);
			dfs(v,u,x),sz[y]=0;
			ed[v].push_back(u);
			ed[u].push_back(v);
		}
	}
	return 0;
}

上一题