luogu P5163 WD与地图 - tarjan - 整体二分 - 线段树合并 - 并查集

题目大意:
给你一张有向图,支持点权修改、删边、询问某个点所在强连通分量的点权前k大的点权之和。
题解:首先将操作反过来问题变为加边。如果能知道何时会合并若干强连通分量为一个大强连通分量,那么就是永无乡这个题了,直线平衡树启发式合并/线段树合并即可。

考虑两个点 x , y x,y 在同一个强连通分量的最早时间 T ( x , y ) T(x,y) ,即为找一条路径 P ( x , y ) P(x,y) ,使得 max ( u , v ) P ( x , y ) T ( u , v ) \max_{(u,v)\in P(x,y)}T(u,v) 最小。即 T ( x , y ) = min P ( x , y ) max ( u , v ) P ( x , y ) T ( u , v ) T(x,y)=\min_{P(x,y)}\max_{(u,v)\in P(x,y)}T(u,v) 。正确性显然。那么只要算出每条边的 T ( u , v ) T(u,v) ,那么按照这个顺序加入边,第一次 x , y x,y 连通的时间就是 T ( x , y ) T(x,y)

那么最后怎么对每条边算第一次在一个强连通分量的时间,这个可以考虑整体二分,即solve(L,R,s,t)表示第L到第R条边,T(e)都在s~t的时间内。并且已经加入了1 ~ (L-1)这些边。令 m i d = s + t 2 mid=\frac{s+t}2 ,加入出现时间 m i d \le mid 的边(加边的时候到并查集里面查一下并查集顶点)。然后跑一个tarjan,然后一条边的T小于等于mid等价于其出现时间小于等于mid并且这个时间已经强连通了。递归即可。
最后那个带撤销并查集只要弄个数组维护一下即可。

// luogu-judger-enable-o2
#include<bits/stdc++.h>
#define rep(i,a,b) for(int i=a;i<=b;i++)
#define Rep(i,v) rep(i,0,(int)v.size()-1)
#define lint long long
#define ull unsigned lint
#define db long double
#define pb push_back
#define mp make_pair
#define fir first
#define sec second
#define gc getchar()
#define debug(x) cerr<<#x<<"="<<x
#define sp <<" "
#define ln <<endl
using namespace std;
typedef pair<int,int> pii;
typedef set<int>::iterator sit;
inline int inn()
{
    int x,ch;while((ch=gc)<'0'||ch>'9');
    x=ch^'0';while((ch=gc)>='0'&&ch<='9')
        x=(x<<1)+(x<<3)+(ch^'0');return x;
}
const int N=100010,M=200010,Q=200010;
pair<int,pii> es[M],Les[M],Res[M];int etm[M];
struct UPDS{
    int x,y,t,tp;
}upds[Q];map<pii,int> eid;int val[N];
namespace segment_merge_space{
    const int T=5000000;int m;
    int ch[T][2],rt[N],fa[N],sz[T],node_cnt;lint s[T];
    inline int _init(int n) { rep(i,1,n) fa[i]=i;return 0; }
    inline int findf(int x) { return x==fa[x]?x:fa[x]=findf(fa[x]); }
    inline int push_up(int x) { return s[x]=s[ch[x][0]]+s[ch[x][1]],sz[x]=sz[ch[x][0]]+sz[ch[x][1]]; }
    inline int build(int p,int l,int r)
    {
        int x=++node_cnt,mid=(l+r)>>1;sz[x]=1,s[x]=p;if(l==r) return x;
        if(p<=mid) ch[x][0]=build(p,l,mid);else ch[x][1]=build(p,mid+1,r);
        return x;
    }
    inline int build(int x,int p) { return val[x]=p,rt[x]=build(p,1,m),0; }
    inline int _merge_(int x,int y,int l,int r)
    {
        if(!x||!y) return x+y;int mid=(l+r)>>1;
        if(l==r) return sz[x]+=sz[y],s[x]+=s[y],x;
        ch[x][0]=_merge_(ch[x][0],ch[y][0],l,mid);
        ch[x][1]=_merge_(ch[x][1],ch[y][1],mid+1,r);
        return push_up(x),x;
    }
    inline int _merge(int x,int y) { return x=findf(x),y=findf(y),(x!=y?rt[x]=_merge_(rt[x],rt[y],1,m),fa[y]=x:0); }
    inline lint query(int x,int l,int r,int k)
    {
        if(!x||k>=sz[x]) return s[x];if(!k) return 0;
        if(l==r) return (lint)l*k;int mid=(l+r)>>1;
        if(k<=sz[ch[x][1]]) return query(ch[x][1],mid+1,r,k);
        return s[ch[x][1]]+query(ch[x][0],l,mid,k-sz[ch[x][1]]);
    }
    inline lint kths(int x,int k) { return x=findf(x),query(rt[x],1,m,k); }
    inline int update(int &x,int l,int r,int p,int sgn)
    {
        if(!x) x=++node_cnt;sz[x]+=sgn,s[x]+=sgn*p;
        if(l==r) return 0;int mid=(l+r)>>1;
        if(p<=mid) update(ch[x][0],l,mid,p,sgn);
        else update(ch[x][1],mid+1,r,p,sgn);return 0;
    }
    inline int update(int x,int v) { int fx=findf(x);return update(rt[fx],1,m,val[x],-1),update(rt[fx],1,m,val[x]+=v,1); }
}using segment_merge_space::_merge;
using segment_merge_space::kths;
using segment_merge_space::update;
using segment_merge_space::kths;
struct union_find{
    int fa[N],sz[N],fas[N],szs[N];
    inline int init(int n) { rep(i,1,n) fa[i]=i,sz[i]=1;return 0; }
    inline int findf(int x) { return x==fa[x]?x:findf(fa[x]); }
    inline int save(int x) { return fas[x]=fa[x],szs[x]=sz[x]; }
    inline int back(int x) { return fa[x]=fas[x],sz[x]=szs[x]; }
    inline int con(int x,int y) { return findf(x)==findf(y); }
    inline int _merge(int x,int y)
    {
        x=findf(x),y=findf(y);if(x==y) return 0;
        if(sz[x]>sz[y]) swap(x,y);return fa[x]=y,sz[y]+=sz[x];
    }
}uf;
namespace tarjan_space{
    int vis[N],sta[N],low[N],dfn[N],lst[N],cnt;stack<int> s;
    struct edges{ int to,pre; }e[M];int h[N],etop,dfc;
    inline int add_edge(int u,int v) { return e[++etop].to=v,e[etop].pre=h[u],h[u]=etop; }
    inline int ins_edge(int x,int y)
    {
        x=uf.findf(x),y=uf.findf(y);
        if(!vis[x]) lst[++cnt]=x,vis[x]=1,uf.save(x);
        if(!vis[y]) lst[++cnt]=y,vis[y]=1,uf.save(y);
        return add_edge(x,y);
    }
    inline int back() { int x;rep(i,1,cnt) x=lst[i],vis[x]=h[x]=sta[x]=0;return etop=cnt=0; }
    inline int uf_back() { rep(i,1,cnt) uf.back(lst[i]);return 0; }
    int tarjan(int x)
    {
        s.push(x),sta[x]=1,dfn[x]=low[x]=++dfc;
        for(int i=h[x],y;i;i=e[i].pre)
            if(!sta[y=e[i].to]) tarjan(y),low[x]=min(low[x],low[y]);
            else if(sta[y]==1) low[x]=min(low[x],dfn[y]);
        if(low[x]==dfn[x])
        {
            while(s.top()^x)
                uf._merge(x,s.top()),sta[s.top()]=2,s.pop();
            s.pop(),sta[x]=2;
        }
        return 0;
    }
    inline int tarjan_and_union()
    {
        int x;while(!s.empty()) s.pop();dfc=0;
        rep(i,1,cnt) if(!sta[x=lst[i]]) tarjan(x);
        return 0;
    }
}using tarjan_space::ins_edge;
inline int solve(int L,int R,int s,int t)
{
    if(L>R||s>t) return 0;
    int mid=(s+t)>>1,cnt=L-1,Lc=0,Rc=0,p;
    rep(i,L,R) if(es[i].fir<=mid)
        ins_edge(es[i].sec.fir,es[i].sec.sec);
    tarjan_space::tarjan_and_union();
    rep(i,L,R) if(es[i].fir<=mid)
    {
        if(uf.con(es[i].sec.fir,es[i].sec.sec)) Les[++Lc]=es[i];
        else Res[++Rc]=es[i];
    }
    rep(i,1,Lc) es[++cnt]=Les[i];p=cnt;
    rep(i,1,Rc) es[++cnt]=Res[i];
    if(s<t) tarjan_space::uf_back(),tarjan_space::back(),solve(L,p,s,mid),solve(p+1,R,mid+1,t);
    else { tarjan_space::back();rep(i,L,R) etm[i]=mid; }
    return 0;
}
vector<lint> ans;
int main()
{
    int n=inn(),m=inn(),q=inn(),u,v,ecnt=0;
    segment_merge_space::m=1e9;
    segment_merge_space::_init(n);
    rep(i,1,n) val[i]=inn();
    rep(i,1,m) u=inn(),v=inn(),es[i].sec=mp(u,v),eid[mp(u,v)]=i;
    for(int i=q;i;i--) upds[i].tp=inn(),upds[i].x=inn(),upds[i].y=inn();
    rep(i,1,q)
        if(upds[i].tp==1) es[eid[mp(upds[i].x,upds[i].y)]].fir=++ecnt;
        else upds[i].t=ecnt;
    rep(i,1,q) if(upds[i].tp==2) val[upds[i].x]+=upds[i].y;
    rep(i,1,n) segment_merge_space::build(i,val[i]);
    sort(es+1,es+m+1),uf.init(n);rep(i,1,m) etm[i]=ecnt+1;
    solve(1,m,0,ecnt+1);
    for(int i=1,j=1;i<=q;i++) if(upds[i].tp>1)
    {
        while(j<=m&&etm[j]<=upds[i].t) _merge(es[j].sec.fir,es[j].sec.sec),j++;
        if(upds[i].tp==2) update(upds[i].x,-upds[i].y);
        else ans.pb(kths(upds[i].x,upds[i].y));
    }
    reverse(ans.begin(),ans.end());
    Rep(i,ans) printf("%lld\n",ans[i]);
    return 0;
}

猜你喜欢

转载自blog.csdn.net/Mys_C_K/article/details/88164645