cf 1416D. Graph and Queries (生成树重构+线段树维护dfs序)

题目链接:传送门

题目思路:思路参考博客 https://www.cnblogs.com/EchoZQN/p/13804989.html

     对于这种题,首先会思考如何转化删边,常用的就是把所有的查询和修改作逆序处理,然后并查集求解,但是这道题由于有修改(查询一个最大值后删除),因此不能采用这个方法。

     那么这道题可以采用类似于kruskal 生成树重构的方法建图,那么对于每一个连通块的所有点都是位于同一棵子树的(子树的根是连通块的祖先节点),不论是删边前的大连通块,还是删边后可能形成的小连通块,这样以来也能保证每一个连通块的所有dfs序是连续的,那么就可以用线段树来维护最值了。

代码:

#include<bits/stdc++.h>
#pragma GCC optimize(2)
using namespace std;
typedef long long LL;
typedef unsigned long long uLL;
typedef pair<int,int> pii;
typedef pair<LL,LL> pLL;
typedef pair<double,double> pdd;
const int N=5e5+5;
const int M=8e5+5;
const int inf=0x3f3f3f3f;
const LL mod=1e8+7;
const double eps=1e-8;
const long double pi=acos(-1.0L);
#define ls (i<<1)
#define rs (i<<1|1)
#define fi first
#define se second
#define pb push_back
#define eb emplace_back
#define mk make_pair
#define mem(a,b) memset(a,b,sizeof(a))
LL read()
{
    LL x=0,t=1;
    char ch;
    while(!isdigit(ch=getchar())) if(ch=='-') t=-1;
    while(isdigit(ch)){ x=10*x+ch-'0'; ch=getchar(); }
    return x*t;

}
int n,m,q,p[N],f[N],vis[N],id,pp[N];
pii a[N],b[N];
int rt[N],c[N<<2],in[N],out[N],rk[N],now;
vector<int> e[N];
int getf(int x)
{
    return f[x]==x?f[x]:f[x]=getf(f[x]);
}
void build(int i,int l,int r)
{
    if(l==r) return (void)(c[i]=p[rk[l]]);
    int mid=l+r>>1;
    build(ls,l,mid);
    build(rs,mid+1,r);
    c[i]=max(c[ls],c[rs]);
}
void update(int i,int l,int r,int pos,int x)
{
    if(l==r) return (void)(c[i]=x);
    int mid=l+r>>1;
    if(pos<=mid) update(ls,l,mid,pos,x);
    else update(rs,mid+1,r,pos,x);
    c[i]=max(c[ls],c[rs]);
}
int query(int i,int l,int r,int ll,int rr)
{
    if(ll<=l&&r<=rr) return c[i];
    int mid=l+r>>1,t1=0,t2=0;
    if(mid>=ll) t1=query(ls,l,mid,ll,rr);
    if(mid<rr) t2=query(rs,mid+1,r,ll,rr);
    return max(t1,t2);
}
void dfs(int u)
{
   // printf("u = %d\n",u);
    in[u]=++id;
    rk[id]=u;
    for(auto v:e[u]) dfs(v);
    out[u]=id;
}
int main()
{
    n=read(),m=read(),q=read();
    for(int i=1;i<=n;i++) p[i]=read();
    for(int i=1;i<=n;i++) pp[p[i]]=i;
    for(int i=1;i<=n;i++) f[i]=i;
    for(int i=1;i<=m;i++)
    {
        int x=read(),y=read();
        a[i]=mk(x,y);
    }
    for(int i=1;i<=q;i++)
    {
        int x=read(),y=read();
        b[i]=mk(x,y);
        if(x==2) vis[y]=1;
    }
    now=n;
    for(int i=1;i<=m;i++)
    {
        if(vis[i]) continue;
        int x=getf(a[i].fi),y=getf(a[i].se);
        if(x==y) continue;
        ++now;
        f[now]=now;
        f[x]=f[y]=now;
        e[now].eb(x);
        e[now].eb(y);
    }
    for(int i=q;i;i--)
    {
        if(b[i].fi==2)
        {
            int j=b[i].se;
            int x=getf(a[j].fi),y=getf(a[j].se);
            if(x==y) continue;
            ++now;
            f[now]=now;
            f[x]=f[y]=now;
            e[now].eb(x);
            e[now].eb(y);
        }
        else rt[i]=getf(b[i].se);//对于i时刻来说 1~i-1这段时间 边已经断掉了,此时祖先代表了这个小的连通块;也可以给每个点加入时间权,自底向上权值不上升,采用树上倍增即可
    }
    for(int i=1;i<=now;i++)
        if(i==f[i]) dfs(i);

    build(1,1,now);
    for(int i=1;i<=q;i++)
    {
        if(b[i].fi==2) continue;
        //printf("rt = %d\n",rt[i]);
        int ans=query(1,1,now,in[rt[i]],out[rt[i]]);
        printf("%d\n",ans);
        //printf("%d %d\n",ans,in[pp[ans]]);
        if(ans) update(1,1,now,in[pp[ans]],0);
    }
    return 0;
}
View Code

 

posted @ 2020-11-07 18:39  DeepJay  阅读(113)  评论(0编辑  收藏  举报