暑假 wzy 鸽鸽讲的自己出的题现在才抽时间补,大惭愧。
首先如果这个题是无向图那么就秒了,可惜不是。那么区别在哪里?我们发现一条有向边加入后连接的两个点可能会在更晚的一个时间点才强连通,不能直接并查集合并。
不过很容易发现这个东西是具有二分性的,于是我们秒掉了单个询问。
那多个询问怎么办呢?我们不能每次都 \(O(n\log n)\) 二分一遍,所以考虑整体二分。
具体来说,对于当前二分区间和询问区间,我们把中点以前的点加进去跑一遍 Tarjan,用可撤销并查集维护强连通关系。对于已经强连通的点进入左区间,否则还不强连通,进入右区间。
具体实现比较考验技巧和耐心,可以参考混乱的代码:
注意空间不要太大也不要太小,考虑好每个数组的用途再开空间。我因为懒看一直 RE 就直接往大里开了,不要学习这种行为
const int N=400010;
int n,m,Q,a[N],t[N<<2],cnt;map<pii,int> qwq;
struct Edge {
int to,nxt;
}e[N<<1];
int hd[N],cn;
il void ade(int u,int v){
e[++cn].to=v,e[cn].nxt=hd[u],hd[u]=cn;
}
struct Query {
int opt,u,v,tm;
bool operator < (const Query &rhs)const{
return tm==rhs.tm?opt<rhs.opt:tm<rhs.tm;
}
}q[N<<1],p[N<<1];
int f[N],siz[N];stack<pii > stk;
int F(int k){
return f[k]==k?k:F(f[k]);
}
void Unify(int u,int v){
u=F(u),v=F(v);if(u==v)return;
if(siz[u]<siz[v])swap(u,v);
f[v]=u,siz[u]+=siz[v],stk.push(mkp(u,v));
}
il void Delete(int lmt){
while(stk.size()>lmt){
pii u=stk.top();stk.pop();
f[u.y]=u.y,siz[u.x]-=siz[u.y];
}
}
int dfn[N],low[N],tim;bool vis[N];stack<int> st;
void Tarjan(int u){
dfn[u]=low[u]=++tim,vis[u]=1,st.push(u);
for(int i=hd[u];i;i=e[i].nxt){
int v=e[i].to;
if(!dfn[v])Tarjan(v),low[u]=min(low[u],low[v]);
else if(vis[v])low[u]=min(low[u],dfn[v]);
}
if(dfn[u]==low[u]){
while(st.top()!=u){
int v=st.top();st.pop(),vis[v]=0,Unify(u,v);
}
vis[st.top()]=0,st.pop();
}
}
Query ql[N<<1],qr[N<<1];
void Solve(int l,int r,int L,int R){
if(L>R)return;
if(l==r){
for(int i=L;i<=R;i++)q[i].tm=l;
return;
}
vector<int> nd;int qlt=0,qrt=0,lst=stk.size();
for(int i=L;i<=R;i++){
if(q[i].tm>nmid)continue;
int u=F(q[i].u),v=F(q[i].v);
nd.pub(u),nd.pub(v),ade(u,v);
}
for(int i:nd)if(!dfn[i])Tarjan(i);
for(int i=L;i<=R;i++){
if(q[i].tm<=nmid&&F(q[i].u)==F(q[i].v))ql[++qlt]=q[i];
else qr[++qrt]=q[i];
}
for(int i=1;i<=qlt;i++)q[L+i-1]=ql[i];
for(int i=1;i<=qrt;i++)q[L+i+qlt-1]=qr[i];
cn=tim=0;
for(int i:nd)hd[i]=dfn[i]=low[i]=0;
Solve(nmid+1,r,L+qlt,R),Delete(lst);
Solve(l,nmid,L,L+qlt-1);
}
struct Node {
int l,r,cnt,w;
}tr[N*20];
int rt[N],tot;
il void Pushup(int k){
tr[k].cnt=tr[ls].cnt+tr[rs].cnt;
tr[k].w=tr[ls].w+tr[rs].w;
}
void Modify(int &k,int pos,int v,int l,int r){
if(!k)k=++tot;
tr[k].cnt+=v,tr[k].w+=v*t[pos];
if(l==r)return;
if(pos<=nmid)Modify(ls,pos,v,l,nmid);
else Modify(rs,pos,v,nmid+1,r);
}
LL Query(int k,int v,int l,int r){
if(tr[k].cnt<=v)return tr[k].w;
if(l==r)return t[l]*v;
if(v<=tr[rs].cnt)return Query(rs,v,nmid+1,r);
else return tr[rs].w+Query(ls,v-tr[rs].cnt,l,nmid);
}
int Merge(int u,int v,int l,int r){
if(!u||!v)return u+v;
if(l==r){
tr[u].cnt+=tr[v].cnt,tr[u].w+=tr[v].w;return u;
}
tr[u].l=Merge(tr[u].l,tr[v].l,l,nmid);
tr[u].r=Merge(tr[u].r,tr[v].r,nmid+1,r);
return Pushup(u),u;
}
int ans[N];
signed main(){
Read(n),Read(m),Read(Q);
for(int i=1;i<=n;i++)f[i]=i,siz[i]=1;
for(int i=1;i<=n;i++)Read(a[i]),t[++cnt]=a[i];
for(int i=1;i<=m;i++){
Read(q[i].u),Read(q[i].v),qwq[mkp(q[i].u,q[i].v)]=i;
}
for(int i=1,u,v;i<=Q;i++){
Read(p[i].opt),Read(u),Read(v),p[i].tm=Q-i+1;
p[i].u=u,p[i].v=v;
if(p[i].opt==1)q[qwq[mkp(u,v)]].tm=p[i].tm;
else if(p[i].opt==2)a[u]+=v,cnt++,a[cnt]=t[cnt]=a[u];
}
sort(t+1,t+1+cnt);int tmp=unique(t+1,t+1+cnt)-t-1;
Solve(0,Q+1,1,m);
for(int i=1;i<=n;i++)f[i]=i,siz[i]=1;
for(int i=1;i<=n;i++){
int qwq=lower_bound(t+1,t+1+tmp,a[i])-t;
Modify(rt[i],qwq,1,1,tmp);
}
for(int i=1;i<=Q;i++)if(p[i].opt!=1)q[++m]=p[i];
sort(q+1,q+m+1);
for(int i=1;i<=m;i++){
int u=q[i].u,v=q[i].v;
if(q[i].opt==0){
u=F(u),v=F(v);
if(u==v)continue;
if(siz[u]<siz[v])swap(u,v);
siz[u]+=siz[v],f[v]=u;
rt[u]=Merge(rt[u],rt[v],1,tmp);
}else if(q[i].opt==2){
int fa=F(u);
int qwq=lower_bound(t+1,t+1+tmp,a[u])-t;
a[u]-=v;
int awa=lower_bound(t+1,t+1+tmp,a[u])-t;
Modify(rt[fa],qwq,-1,1,tmp);
Modify(rt[fa],awa,1,1,tmp);
}else {
int fa=F(u);
ans[Q-q[i].tm+1]=Query(rt[fa],v,1,tmp);
}
}
for(int i=1;i<=Q;i++){
if(p[i].opt==3)printf("%lld\n",ans[i]);
}
KafuuChino HotoKokoa
}