[bzoj3123][启发式合并][主席树]森林

14 篇文章 0 订阅
4 篇文章 0 订阅

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。之后的操作类似。
这里写图片描述

题解

说实话我现在还是没有搞懂启发式合并的神奇复杂度
据说可以做到logn?
由于连接后保证是森林,对于两个连通块,我们可以找到块内节点数较少的那个块,把他暴力合并进节点数较多的那个块。这个用并查集实现
区间寻找K小值直接主席树上树,暴力合并的时候先清零原主席树,然后再add与合并。记得把两个点连边与更新树上倍增的数组
查询的时候找到lca然后瞎搞一下。
复杂度nlogn^2

#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<algorithm>
#include<cmath>
using namespace std;
struct LSnode{int y,p;}w[210000];int rank[210000];//第i个数的排名 
bool cmp(LSnode n1,LSnode n2){return n1.y<n2.y;}
struct trnode
{
    int lc,rc,c;    
}tr[21100000];int trlen;
struct node
{
    int x,y,next;
}a[210000];int len,last[210000];
int n,m,T,lastans;
void ins(int x,int y){len++;a[len].x=x;a[len].y=y;a[len].next=last[x];last[x]=len;}
int ffa[210000];
int findfa(int x){if(ffa[x]!=x)ffa[x]=findfa(ffa[x]);return ffa[x];}
int tot[210000],rt[210000];
void add(int &now,int l,int r,int p)
{
    if(now==0)now=++trlen;
    tr[now].c++;
    if(l==r)return ;
    int mid=(l+r)/2;
    if(p<=mid)add(tr[now].lc,l,mid,p);
    else add(tr[now].rc,mid+1,r,p);
}
void merge(int &x,int y)
{
    if(x==0){x=y;return ;}
    if(y==0)return ;
    tr[x].c+=tr[y].c;
    merge(tr[x].lc,tr[y].lc);
    merge(tr[x].rc,tr[y].rc);
}
int findKth(int x,int y,int u,int v,int l,int r,int K)
{
    if(l==r)return w[l].y;
    int mid=(l+r)/2;
    int c=tr[tr[x].lc].c+tr[tr[y].lc].c-tr[tr[u].lc].c-tr[tr[v].lc].c;
    if(K<=c)return findKth(tr[x].lc,tr[y].lc,tr[u].lc,tr[v].lc,l,mid,K);
    else return findKth(tr[x].rc,tr[y].rc,tr[u].rc,tr[v].rc,mid+1,r,K-c);
}
void del(int now)
{
    tr[now].c=0;
    int lc=tr[now].lc,rc=tr[now].rc;
    if(tr[lc].c>0 && rc)del(lc);
    if(tr[rc].c>0 && lc)del(rc);
    tr[now].lc=tr[now].rc=0;
}
int bin[25],fa[110000][25],dep[110000];
void ch(int x,int f)
{
    dep[x]=dep[f]+1;fa[x][0]=f;
    del(rt[x]);add(rt[x],1,n,rank[x]);merge(rt[x],rt[f]);
    for(int i=1;bin[i]<=dep[x];i++)fa[x][i]=fa[fa[x][i-1]][i-1];
    for(int k=last[x];k;k=a[k].next)
    {
        int y=a[k].y;
        if(y!=f)ch(y,x);
    }
}
void pre_tree_node(int x)
{
    for(int i=1;bin[i]<=dep[x];i++)fa[x][i]=fa[fa[x][i-1]][i-1];
    add(rt[x],1,n,rank[x]);merge(rt[x],rt[fa[x][0]]);
    for(int k=last[x];k;k=a[k].next)
    {
        int y=a[k].y;
        if(y!=fa[x][0])
        {
            fa[y][0]=x;
            dep[y]=dep[x]+1;
            pre_tree_node(y);
        }
    }
}
int lca(int x,int y)
{
    if(dep[x]<dep[y])swap(x,y);
    for(int i=20;i>=0;i--)if(bin[i]<=dep[x] && dep[fa[x][i]]>=dep[y])x=fa[x][i];
    if(x==y)return x;
    for(int i=20;i>=0;i--)if(bin[i]<=dep[x] && fa[x][i]!=fa[y][i])x=fa[x][i],y=fa[y][i];
    return fa[x][0];
}
char ss[10];
int main()
{
    int TT;
    scanf("%d",&TT);
    len=0;memset(last,0,sizeof(last));
    memset(rt,0,sizeof(rt));
    trlen=0;
    scanf("%d%d%d",&n,&m,&T);
    for(int i=1;i<=n;i++)scanf("%d",&w[i].y),w[i].p=i;
    sort(w+1,w+1+n,cmp);
    //n=0;
    for(int i=1;i<=n;i++)rank[w[i].p]=i;
    bin[0]=1;for(int i=1;i<=20;i++)bin[i]=bin[i-1]*2;
    len=0;memset(last,0,sizeof(last));
    for(int i=1;i<=n;i++)ffa[i]=i,tot[i]=1;
    for(int i=1;i<=m;i++)
    {
        int x,y;scanf("%d%d",&x,&y);
        ins(x,y);ins(y,x);
        int p=findfa(x),q=findfa(y);
        if(p!=q)
        {
            ffa[p]=q;
            tot[q]+=tot[p];
        }
    }
    for(int i=1;i<=n;i++)
        if(dep[i]==0)
        {
            fa[i][0]=0;dep[i]=1;pre_tree_node(i);
        }
    lastans=0;
    while(T--)
    {
        int u,v,k;
        scanf("%s%d%d",ss+1,&u,&v);
        u^=lastans;v^=lastans;
        if(ss[1]=='Q')
        {
            scanf("%d",&k);k^=lastans;
            int tmp=lca(u,v);
            lastans=findKth(rt[u],rt[v],rt[tmp],rt[fa[tmp][0]],1,n,k);
            printf("%d\n",lastans);
        }
        else
        {
            int p=findfa(u),q=findfa(v);
            int tx=tot[p],ty=tot[q];
            if(tx<ty)
            {
                ins(u,v);ins(v,u);
                ch(u,v);
                tot[q]+=tot[p];ffa[p]=q;
            }
            else
            {
                ins(u,v);ins(v,u);
                ch(v,u);
                tot[p]+=tot[q];ffa[q]=p;
            }
        }
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值