[SDOI2013] 森林

42 篇文章 0 订阅
18 篇文章 1 订阅

题目描述:

给出一个森林
求 1 : u -> v 路径上的第K小值
2:u v 合并 u v 所在树

题目分析:

合并?LCT?
LCT没法维护第K值啊(雾
动态维护第K值肯定是要用主席树的
在树上建立主席树,我们新的节点继承父亲树信息
查询的时候求LCA,然后消除影响
合并的话我们就搞一下启发式合并qwq
OQlogn2 O ( Q ∗ l o g n 2 )

题目链接:

BZOJ 3123
Luogu 3302

Ac 代码:

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
const int maxm=8e4+10; 
int ls[maxm*500],rs[maxm*500],sum[maxm*500],siz[maxm];
int rt[maxm],sz,t;
int fa[maxm],f[maxm][18];
int hash[maxm],val[maxm],deep[maxm];
int find(int x){return fa[x]==x?x:fa[x]=find(fa[x]);}
bool vis[maxm];
int n,m,q;
int head[maxm],to[maxm<<1],net[maxm<<1],cnt;
void addedge(int u,int v)
{cnt++;to[cnt]=v,net[cnt]=head[u],head[u]=cnt;}
void insert(int &now,int pre,int l,int r,int ind)
{
    now=++sz;
    ls[now]=ls[pre],rs[now]=rs[pre],sum[now]=sum[pre]+1;
    if(l>=r) return;
    int mid=(l+r)>>1;
    if(ind<=mid) insert(ls[now],ls[pre],l,mid,ind);
    else insert(rs[now],rs[pre],mid+1,r,ind);
}
int find_kth(int s,int t,int lca,int lcaf,int l,int r,int rank)
{
    int nowsum=sum[ls[s]]+sum[ls[t]]-sum[ls[lca]]-sum[ls[lcaf]];
    if(l>=r) return l;
    int mid=(l+r)>>1;
    if(nowsum>=rank) return find_kth(ls[s],ls[t],ls[lca],ls[lcaf],l,mid,rank);
    else return find_kth(rs[s],rs[t],rs[lca],rs[lcaf],mid+1,r,rank-nowsum);
}
int dfs(int now,int fax,int root)
{
    f[now][0]=fax;
    for(int i=1;i<=16;i++)
    f[now][i]=f[f[now][i-1]][i-1];
    siz[root]++,deep[now]=deep[fax]+1,fa[now]=root;vis[now]=1;
    insert(rt[now],rt[fax],1,t,val[now]);
    for(int i=head[now];i;i=net[i])
    if(to[i]!=fax) dfs(to[i],now,root);
}
int LCA(int u,int v)
{
    if(deep[u]<deep[v]) std::swap(u,v);
    //printf("%d %d\n",u,v);
    for(int i=16;~i;i--)
    {
        //printf("%d %d %d %d\n",deep[u],deep[v],f[u][i],deep[f[u][i]]);
        if(deep[f[u][i]]>=deep[v]) u=f[u][i];
    }

    if(u==v) return u;
    for(int i=16;~i;i--)
    if(f[u][i]!=f[v][i]) 
     u=f[u][i],v=f[v][i];
    return f[u][0];
}
int main()
{
    int T;
    scanf("%d",&T);
    scanf("%d%d%d",&n,&m,&q);
    for(int i=1;i<=n;i++)
     scanf("%d",&val[i]),hash[i]=val[i],fa[i]=i;
    std::sort(hash+1,hash+n+1);
    t=std::unique(hash+1,hash+n+1)-hash-1;
    for(int i=1;i<=n;i++)
     val[i]=std::lower_bound(hash+1,hash+t+1,val[i])-hash;
    for(int i=1;i<=m;i++)
    {
        int u,v;
        scanf("%d%d",&u,&v);
        addedge(u,v),addedge(v,u);
    }
    int tot=0;
    for(int i=1;i<=n;i++)
    if(!vis[i])
     dfs(i,0,++tot),fa[tot]=tot;
    //for(int i=0;i<=n;i++) printf("%d\n",deep[i]);
    int last=0;
    for(int i=1,x,y;i<=q;i++)
    {
        char s[10];
        scanf("%s%",s);
        if(s[0]=='Q')
        {
            int k;
            scanf("%d%d%d",&x,&y,&k);
            x^=last,y^=last;
            k^=last;
            int lca=LCA(x,y);
            last=hash[find_kth(rt[x],rt[y],rt[lca],rt[f[lca][0]],1,t,k)];
            printf("%d\n",last);
        }
        else
        {   
            scanf("%d%d",&x,&y);
            x^=last,y^=last;
            addedge(x,y),addedge(y,x);
            int f1=find(x),f2=find(y);
            if(siz[f1]<siz[f2]) std::swap(f1,f2),std::swap(x,y);
            dfs(y,x,f1);
        } 
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值