P3302 [SDOI2013] 森林

P3302 [SDOI2013] 森林

题目描述

小 Z 有一片森林,含有 \(N\) 个节点,每个节点上都有一个非负整数作为权值。初始的时候,森林中有 \(M\) 条边。

小Z希望执行 \(T\) 个操作,操作有两类:

  • Q x y k 查询点 \(x\) 到点 \(y\) 路径上所有的权值中,第 \(k\) 小的权值是多少。此操作保证点 \(x\) 和点 \(y\) 连通,同时这两个节点的路径上至少有 \(k\) 个点。
  • 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 完成这些操作。

输入格式

第一行包含一个正整数 \(\mathrm{testcase}\),表示当前测试数据的测试点编号。

第二行包含三个整数 \(N,M,T\),分别表示节点数、初始边数、操作数。

第三行包含 \(N\) 个非负整数表示 \(N\) 个节点上的权值。

接下来 \(M\) 行,每行包含两个整数 \(x\)\(y\),表示初始的时候,点 \(x\) 和点 \(y\) 之间有一条无向边。

接下来 \(T\) 行,每行描述一个操作,格式为 Q x y k 或者 L x y,其含义见题目描述部分。

输出格式

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


数据范围

对于 \(100\%\) 的测试数据,所有节点的编号在 \(1\sim N\) 的范围内。节点上的权值 \(\le 10^9\)\(M<N\)\(N\le 8\times 10^4\)

Solution:

看到题目,花费 0.1 个思考想到了 LCT 然后发现“路径上权值第 k 大”。是 LCT 无法战胜的东西了。

然后又花费 0.5 个思考,发现 “路径上权值第 k 大” 显然是主席树的活啊。然后我们发现他只 link ,不 cut 的啊!!! 并且我们还发现他还保证了连完边之后是一个森林。这就完美符合了我们使用线段树的设想。

首先,用主席树维护路径上权值第 k 大的做法很模板,就是每次二分一个数 \(val\) ,然后查询一下路径上满足 \(i<val\)\(i\) 的个数 \(cnt\)。 然后我们取出路径上的点的方法也很简单,就是线段树上的每个节点对应原树的各个节点,线段树节点的区间下存有其子树内所有节点的权值。取出一条路径时,由于我们并不关心满足条件的 \(i\) 的具体权值,我们只需要将 \(x,y,lca,lca'father\) 这四个点的cnt 按照 \(cnt_x+cnt_y-cnt_lca-cnt_{lca'sfather}\) 来做一个差分就好了。

接下来是连边操作:我们如果每次连边都直接将两个联通块暴力合并然后扫描一遍,那么时间复杂度是很容易爆炸的。但是我们发现,在无向图中添加一条边来合并两个联通块时,这条在森林上对应的父子关系其实是不重要的,所以我们每次合并时只需要将 \(size\) 小的联通块重新扫描一遍,暴力合并到大的联通块上就好了,这样做的时间复杂度是 \(O(nlogn)\) 的(其实就是 dsu on tree)

Code:

#include<bits/stdc++.h>
const int N=8e4+5;
const int lg=20;
using namespace std;
struct Edge{
    int to,nxt;
}e[N<<2];
int n,m,T,t_cnt,e_cnt,tot,ans;
int head[N],a[N],b[N],dep[N],siz[N],root[N],fa[N];
int f[N][lg+5];
char c[999];
int id(int x)
{
    return lower_bound(b+1,b+1+n,x)-b;
}
int find(int x)
{
    fa[x] = fa[x]==x ? x :find(fa[x]);
    return fa[x];
}
void add(int x,int y)
{
    e[++e_cnt]=(Edge){y,head[x]};
    head[x]=e_cnt;
}
struct Tree{
    int ls,rs,cnt;
}t[N*170];
void build(int &now,int l,int r)
{
    now=++t_cnt;
    if(l==r)return;
    int mid=l+r>>1;
    build(t[now].ls,l,mid);
    build(t[now].rs,mid+1,r);
    return ;
}
void insert(int &now,int pre,int l,int r,int k)
{
    t[now=++t_cnt]=t[pre];
    t[now].cnt++;
    if(l==r)return ;
    int mid=l+r>>1;
    if(k<=mid)insert(t[now].ls,t[pre].ls,l,mid,k);
    else insert(t[now].rs,t[pre].rs,mid+1,r,k);
}
int query(int x,int y,int lca,int f_lca,int l,int r,int k)
{
    if(l==r)return b[l];
    int val=t[t[x].ls].cnt+t[t[y].ls].cnt-t[t[lca].ls].cnt-t[t[f_lca].ls].cnt;
    int mid=l+r>>1;
    if(k<=val)return query(t[x].ls,t[y].ls,t[lca].ls,t[f_lca].ls,l,mid,k);
    else return query(t[x].rs,t[y].rs,t[lca].rs,t[f_lca].rs,mid+1,r,k-val);
}
void dfs(int x,int y,int rt)
{
    f[x][0]=y;
    for(int i=1;i<=lg;i++)
    {
        f[x][i]=f[f[x][i-1]][i-1];
    }
    siz[rt]++;
    fa[x]=y;
    dep[x]=dep[y]+1;
    insert(root[x],root[y],1,tot,id(a[x]));
    for(int i=head[x];i;i=e[i].nxt)
    {
        int to=e[i].to;
        if(to==y)continue;
        dfs(to,x,rt);
    }
}
int LCA(int x,int y)
{
    if(x==y)return x;
    if(dep[x]<dep[y])swap(x,y);
    for(int i=lg;~i;i--)
    {
        if(dep[f[x][i]]>=dep[y])x=f[x][i];
    }
    if(x==y)return x;
    for(int i=lg;~i;i--)
    {
        if(f[x][i]!=f[y][i])x=f[x][i],y=f[y][i];
    }
    return f[x][0];
}
void work()
{
    cin>>n;
    cin>>n>>m>>T;
    for(int i=1;i<=n;i++)
    {
        scanf("%d",&a[i]);
        b[i]=a[i];
        fa[i]=i;
    }
    sort(b+1,b+1+n);
    tot=unique(b+1,b+1+n)-(b+1);
    build(root[0],1,tot);
    for(int i=1,x,y;i<=m;i++)
    {
        scanf("%d%d",&x,&y);
        add(x,y);
        add(y,x);
    }
    //cout<<111;
    for(int i=1;i<=n;i++)
    {
        if(!dep[i])
        {
            dfs(i,0,i);
            fa[i]=i;
        }
    }
    for(int i=1,x,y,k;i<=T;i++)
    {
        scanf("%s",c);
        if(c[0]=='Q')
        {
            scanf("%d%d%d",&x,&y,&k);
            x^=ans,y^=ans,k^=ans;
            int lca=LCA(x,y);
            ans=query(root[x],root[y],root[lca],root[f[lca][0]],1,tot,k);
            printf("%d\n",ans);
        }
        else
        {
            scanf("%d%d",&x,&y);
            x^=ans,y^=ans;
            int xx=find(x),yy=find(y);
            if(siz[xx]<siz[yy]){swap(x,y),swap(xx,yy);}
            add(x,y),add(y,x);
            dfs(y,x,xx);
        }
    }
}
int main()
{
    //freopen("forest.in","r",stdin);freopen("forest.out","w",stdout);
    work();
    return 0;
}
posted @ 2025-08-06 14:10  liuboom  阅读(3)  评论(0)    收藏  举报