题目
题目链接:https://www.luogu.com.cn/problem/P5163
CX 让 WD 研究的地图可以看做是 (n) 个点,(m) 条边的有向图,由于政府正在尝试优化人民生活,他们会废弃一些无用的道路来把省下的钱用于经济建设。
城市都有各自的发达程度 (s_i)。为了方便管理,政府将整个地图划分为一些地区,两个点 (u,v) 在一个地区当且仅当 (u,v) 可以互相到达。政府希望知道一些时刻某个地区的前 (k) 发达城市的发达程度总和,以此推断建设的情况。
也就是说,共有三个操作:
1 a b
表示政府废弃了从 (a) 连向 (b) 的边,保证这条边存在。2 a b
表示政府把钱用于建设城市 (a),使其发达程度增加 (b)。3 a b
表示政府希望知道 (a) 城市所在地区发达程度前 (b) 大城市的发达程度之和。如果地区中的城市不足 (b) 个输出该地区所有城市的发达程度总和。
(nleq 10^5,m,Qleq 2 imes 10^5)。
思路
题目中是有向图,也就是两个点在同一个强连通分量内时,才会同时在一个地区内。
考虑无向图时怎么做。时间倒流变为加边,并查集维护图的连通情况,然后用权值线段树合并即可。询问就在权值线段树上二分一下。
有向图的话,我们就考虑每一条边所连接的两个点什么时候会开始在同一个强连通分量内。考虑整体二分。对于当前的时间区间 ([l,r]) 以及边的区间 ([ql,qr]),我们把所有出现时间晚于 (mid) 的边加入图中,跑 tarjan 求强连通分量,并用并查集维护缩点。由于在回溯时需要撤销缩点,采用可撤销并查集即可。
然后便利该区间所有候选的边,如果这条边所连接的两个点处于同一个强连通分量内,那么这条边就扔到右子树内,否则就扔到左子树内。然后先继续二分左子树,撤销并查集后二分右子树。
这样我们就得到了每一条边的两个点处于同一个强连通分量的时间,那么就可以看作是在该时间加入了一条无向边。并查集 + 权值线段树合并即可。
时间复杂度 (O(mlog^2n+Qlog n))。
代码
#include <bits/stdc++.h>
#define mp make_pair
#define int long long
using namespace std;
typedef long long ll;
const int N=400010,LG=20;
int a[N],aa[N],U[N],V[N],father[N],siz[N],head[N],dfn[N],low[N],ans[N],rt[N];
int n,m,cnt,tot,tot1,Q;
bool vis[N];
map<pair<int,int>,bool> vis1;
stack<pair<int,int> > st;
stack<int> st1;
vector<int> d;
struct node1
{
int opt,x,y;
}b[N];
struct node2
{
int id,x,y,t;
}c[N],cc[N];
struct edge
{
int next,to;
}e[N];
bool cmp(node2 x,node2 y)
{
return x.t>y.t;
}
void add(int from,int to)
{
e[++tot]=(edge){head[from],to};
head[from]=tot;
}
int find(int x)
{
return x==father[x]?x:find(father[x]);
}
void merge(int x,int y)
{
x=find(x); y=find(y);
if (x==y) return;
if (siz[x]<siz[y]) swap(x,y);
father[y]=x; siz[x]+=siz[y];
st.push(mp(x,y));
}
void tarjan(int x)
{
dfn[x]=low[x]=++tot;
st1.push(x); vis[x]=1;
for (int i=head[x];~i;i=e[i].next)
{
int v=e[i].to;
if (!dfn[v])
{
tarjan(v);
low[x]=min(low[x],low[v]);
}
else if (vis[v])
low[x]=min(low[x],dfn[v]);
}
if (dfn[x]==low[x])
{
int y;
do {
y=st1.top(); st1.pop();
merge(y,x); vis[y]=0;
} while (y!=x);
}
}
void solve(int l,int r,int ql,int qr)
{
if (l==r)
{
for (int i=ql;i<=qr;i++) c[i].t=l;
return;
}
int mid=(l+r)>>1,tp=st.size();
tot=0; d.clear();
for (int i=ql;i<=qr;i++)
if (c[i].id>mid)
{
int x=find(c[i].x),y=find(c[i].y);
if (x!=y)
{
add(x,y);
d.push_back(x); d.push_back(y);
}
}
tot=0;
for (int i=0;i<d.size();i++)
if (!dfn[d[i]]) tarjan(d[i]);
for (int i=0;i<d.size();i++)
head[d[i]]=-1,dfn[d[i]]=low[d[i]]=0;
int pl=ql-1,pr=qr+1;
for (int i=ql;i<=qr;i++)
if (find(c[i].x)==find(c[i].y))
cc[--pr]=c[i];
else
cc[++pl]=c[i];
for (int i=ql;i<=pl;i++) c[i]=cc[i];
for (int i=qr;i>=pr;i--) c[i]=cc[qr-i+pr];
solve(l,mid,ql,pl);
for (;st.size()>tp;st.pop())
{
int x=st.top().first,y=st.top().second;
siz[x]-=siz[y]; father[y]=y;
}
solve(mid+1,r,pr,qr);
}
struct SegTree
{
int tot,lc[N*LG*4],rc[N*LG*4],cnt[N*LG*4];
ll sum[N*LG*4];
int update(int x,int l,int r,int k,ll v,int v2)
{
if (!x) x=++tot;
sum[x]+=v; cnt[x]+=v2;
if (l==r) return x;
int mid=(l+r)>>1;
if (k<=mid) lc[x]=update(lc[x],l,mid,k,v,v2);
else rc[x]=update(rc[x],mid+1,r,k,v,v2);
return x;
}
int merge(int x,int y)
{
if (!x || !y) return x|y;
sum[x]+=sum[y]; cnt[x]+=cnt[y];
lc[x]=merge(lc[x],lc[y]);
rc[x]=merge(rc[x],rc[y]);
return x;
}
ll query(int x,int l,int r,int k)
{
if (cnt[x]<k) return sum[x];
if (l==r) return 1LL*aa[l]*k;
int mid=(l+r)>>1;
if (cnt[rc[x]]>=k) return query(rc[x],mid+1,r,k);
else return query(lc[x],l,mid,k-cnt[rc[x]])+sum[rc[x]];
}
}seg;
signed main()
{
memset(head,-1,sizeof(head));
scanf("%lld%lld%lld",&n,&m,&Q);
for (int i=1;i<=n;i++) scanf("%lld",&a[i]),aa[++tot1]=a[i];
for (int i=1;i<=m;i++) scanf("%lld%lld",&U[i],&V[i]);
for (int i=1;i<=Q;i++)
{
scanf("%lld%lld%lld",&b[i].opt,&b[i].x,&b[i].y);
if (b[i].opt==1)
{
c[++cnt]=(node2){i,b[i].x,b[i].y,0};
vis1[mp(b[i].x,b[i].y)]=1;
}
if (b[i].opt==2)
a[b[i].x]+=b[i].y,aa[++tot1]=a[b[i].x];
}
sort(aa+1,aa+1+tot1);
tot1=unique(aa+1,aa+1+tot1)-aa-1;
for (int i=1;i<=m;i++)
if (!vis1[mp(U[i],V[i])])
{
b[++Q]=(node1){1,U[i],V[i]};
c[++cnt]=(node2){Q,U[i],V[i],0};
}
for (int i=1;i<=n;i++)
{
father[i]=i; siz[i]=1;
int val=lower_bound(aa+1,aa+1+tot1,a[i])-aa;
rt[i]=seg.update(rt[i],1,tot1,val,a[i],1);
}
solve(0,Q,1,cnt);
sort(c+1,c+1+cnt,cmp);
tot=0;
for (int i=Q,j=1;i>=1;i--)
{
for (;j<=cnt && c[j].t==i;j++)
{
int x=find(c[j].x),y=find(c[j].y);
if (x!=y)
{
if (siz[x]<siz[y]) swap(x,y);
merge(x,y);
rt[x]=seg.merge(rt[x],rt[y]);
}
}
if (b[i].opt==2)
{
int x=b[i].x,y=find(x);
int val=lower_bound(aa+1,aa+1+tot1,a[x])-aa;
rt[y]=seg.update(rt[y],1,tot1,val,-a[x],-1);
a[x]-=b[i].y;
val=lower_bound(aa+1,aa+1+tot1,a[x])-aa;
rt[y]=seg.update(rt[y],1,tot1,val,a[x],1);
}
if (b[i].opt==3)
{
int x=find(b[i].x);
ans[++tot]=seg.query(rt[x],1,tot1,b[i].y);
}
}
for (int i=tot;i>=1;i--)
cout<<ans[i]<<"
";
return 0;
}