洛谷3224 [HNOI2012]永无乡 (splay+启发式合并)

不得不说
现在真的是越来越懒了

很多东西都咕咕咕了啊
qwq

还是回到这个题
首先,涉及到联通块第 k k k大的问题,有两种方法来解决,这里只介绍 s p l a y splay splay

(我果然是已经菜到 s p l a y splay splay都不会了

首先对于每一个点建一个 s p l a y splay splay并加入两个哨兵节点。然后对于题目的连边操作,我们选择进行启发式合并,就是每次合并的是,通过遍历 s i z e size size较小的那个 s p l a y splay splay,把里面的节点依次删掉,然后插到 s i z e size size大的那个 s p l a y splay splay里面。
不过编号的处理似乎并不是很好的处理

我选择的处理方式是,每一个点的在 s p l a y splay splay的节点编号就是他自身的编号,然后插入的时候,我们只会插入那些有用的节点,也就是非哨兵节点

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
#include<vector>
#include<queue>
#include<set>
#include<map>
#define mk make_pair
#define pb push_back
#define ll long long

using namespace std;

inline int read()
{
    int x=0,f=1;char ch=getchar();
    while (!isdigit(ch)){if (ch=='-') f=-1;ch=getchar();}
    while (isdigit(ch)) {x=(x<<1)+(x<<3)+ch-'0';ch=getchar();}
    return x*f;
}

const int maxn = 4e5+1e2;
const int inf = 1e9;

int root[maxn];
int a[maxn];
int n,m;
int ch[maxn][2];
int size[maxn];
int tot;
int fa[maxn];
int val[maxn];
vector<int> rb;
int num[maxn];

int newnode()
{
    int now=0;
    if(rb.empty()) ++tot,now=tot;
    else now = rb.back(),rb.pop_back();
    return now;
}

int delnode(int x)
{
    //rb.push_back(x);
    fa[x]=ch[x][1]=ch[x][0]=size[x]=0;

}

int son(int x)
{
    if(ch[fa[x]][0]==x) return 0;
    else return 1;
}

void update(int x)
{
    size[x]=size[ch[x][1]]+size[ch[x][0]]+1;
}

void rotate(int x)
{
    int y = fa[x],z=fa[y];
    int b = son(x),c = son(y);
    ch[z][c]=x;
    fa[x]=z;
    ch[y][b]=ch[x][!b];
    fa[ch[x][!b]]=y;
    ch[x][!b]=y;
    fa[y]=x;
    update(y);
    update(x);
}

void splay(int x,int p)
{
    while (fa[x]!=p)
    {
    	int y = fa[x],z = fa[y];
    	if (z==p) rotate(x);
    	else
        {
        	if(son(x)==son(y))
        	{
        		rotate(y);
        		rotate(x);
        	}
            else 
            {
            	rotate(x);
            	rotate(x);
            }
        }
   }
   if (p==0) root[x]=x;
}

int find_qq(int root,int x)
{
    int now = root;
    int num=0;
    while(now)
    {
        if (val[now]<x)
        {
            num = now;
            now = ch[now][1];
        }
        else
            now = ch[now][0];
    }
    return num;
}

int find_hj(int root,int x)
{
    int now = root;
    int num=0;
    while (now) 
    {
       if (val[now]>x)
       {
       	  num = now;
       	  now = ch[now][0];
       }
       else 
       	   now = ch[now][1];
    }
    return num;
}

void insert(int root,int x,int p)
{
    int qq = find_qq(root,x);
    int hj = find_hj(root,x);
    splay(qq,0);
    splay(hj,qq);
    int y = ch[hj][0];
    int uu = p;
    fa[uu]=hj;
    ch[hj][0]=uu;
    size[uu]=1;
    val[uu]=x;
    splay(uu,0);
}

void delet(int root,int x)
{
    int qq = find_qq(root,x);
    int hj = find_hj(root,x);
    splay(qq,0);
    splay(hj,qq);
    int y = ch[hj][0];
    delnode(y);
    num[y]=0;
    ch[hj][0]=0;
}

int kth(int root,int x)
{
    int now = root;
    while (1)
    {
        if (x<=size[ch[now][0]]) now = ch[now][0];
        else 
        {
            if (x<=size[ch[now][0]]+1) return now;
            x-=size[ch[now][0]]+1;
            now=ch[now][1];
        }
    }
}

int faa[maxn];
int b[maxn];
int tmp;

int find(int x)
{
    if(faa[x]!=x) faa[x]=find(faa[x]);
    return faa[x];
}

void dfs(int x)
{
    if(x<=n) b[++tmp]=x;
    if (ch[x][0]) dfs(ch[x][0]);
    if (ch[x][1]) dfs(ch[x][1]);
    //cout<<1<<endl;
}

void merge(int x,int y)
{
    int f1 = find(x);
    int f2 = find(y);
    if (f1==f2) return;
    faa[f1]=faa[f2];
    splay(x,0);
    splay(y,0);
    //cout<<1<<endl;
    if(size[x]>size[y]) swap(x,y);
    tmp=0;
    dfs(x);
    for (int i=1;i<=tmp;i++) delnode(b[i]);
    for (int i=1;i<=tmp;i++) insert(root[y],a[b[i]],b[i]);
}

int main()
{
    n=read(),m=read();
    tot=n;
    for (int i=1;i<=n;i++) 
    {
    	root[i]=++tot;
    	val[tot]=inf;
    	size[tot]=1;
    	int pre = tot;
    	++tot;
    	size[tot]=1;
    	val[tot]=-inf;
    	fa[tot]=pre;
    	ch[pre][0]=tot;
    }
    for (int i=1;i<=n;i++) a[i]=read();
    for (int i=1;i<=n;i++) faa[i]=i;
    for (int i=1;i<=n;i++) insert(root[i],a[i],i),splay(i,0);

    for (int i=1;i<=m;i++)
    {
       int x=read(),y=read();
       merge(x,y);
    }
    //for (int i=1;i<=n;i++) cout<<root[i]<<" "<<val[kth(root[i],2)]<<endl;
    //cout<<endl;
    int q=read();
    for (int i=1;i<=q;i++)
    {
    	char s[10];
    	scanf("%s",s+1);
    	int x=read(),y=read();
    	if (s[1]=='Q')
    	{
            splay(x,0);
            y++;
            if (y>size[x]-1) cout<<-1<<"\n";
            else cout<<kth(root[x],y)<<"\n";
        }
        else
        {
        	merge(x,y); 	
        }
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值