P5163-WD与地图【tarjan,整体二分,线段树合并】
正题
题目链接:https://www.luogu.com.cn/problem/P5163
题目大意
给出\(n\)个点\(m\)条有向边,点有权值,要求支持操作
- 删除一条边
- 修改一个点的权值
- 求一个点所在强连通分量中前\(k\)大权值和
\(1\leq n\leq 10^5,1\leq m,q\leq 2\times 10^5\)
解题思路
首先删边肯定是时光倒流改成加边,然后考虑怎么继续做。
我们需要处理一些点集什么时候合并,这样的合并其实不会超过\(n-1\)次。
而且每次肯定是合并某条边\((x,y)\)两个点所在的强连通分量,但是每次加入的一条边\((x,y)\)不一定会即使生效。
我们可以考虑对于每条边求出它在后来加入哪条边加入之后生效了,这个可以考虑整体二分,我们每次把所有询问的边集在\([0,mid]\)区间的边加入然后跑\(tarjan\),把跑出来的强连通分量缩成一个点然后继续丢到右边跑,跑完右边的子区间之后再撤销这次\(tarjan\)缩起来的点然后跑左边。
这样一定是对的因为如果一条答案不在这个区间的边生效,那么它要不就在之前被合并了要么在这个区间内都合并不了,所以没有作用。
这个要用一个可撤销并查集,记得安秩合并就好了。
之后我们就有一个操作变成合并两个集合了,线段树合并做剩下的部分就行了
时间复杂度\(O(n\log^2 n)\)(反正差不多同级就不写这么详细了
)
code
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<map>
#include<stack>
#define ll long long
#define mp(x,y) make_pair(x,y)
using namespace std;
const ll N=8e5+10;
struct node{
ll to,next;
}a[N];
struct enode{
ll x,y,t,T;
}e[N],p1[N],p2[N];
struct qnode{
ll x,k;
}q[N];
struct snode{
ll x,y,fa,dep;
}st[N];
ll n,m,t,tot,snt,clt,cnt,s[N];
ll ls[N],fa[N],dep[N],cl[N];
ll dfn[N],low[N],rt[N],ans[N];
bool ins[N];stack<ll> S;
map<pair<ll,ll> ,ll> emp;
void addl(ll x,ll y){
a[++tot].to=y;
a[tot].next=ls[x];
ls[x]=tot;return;
}
ll find(ll x)
{return (fa[x]==x)?x:find(fa[x]);}
ll Find(ll x)
{return (fa[x]==x)?x:(fa[x]=Find(fa[x]));}
void unionn(ll x,ll y){
x=find(x);y=find(y);
if(x==y)return;
if(dep[x]>dep[y])swap(x,y);
st[++snt]=(snode){x,y,fa[x],dep[y]};
fa[x]=y;dep[y]=max(dep[y],dep[x]+1);
}
void clearto(ll z){
while(snt>z){
dep[st[snt].y]=st[snt].dep;
fa[st[snt].x]=st[snt].fa;
snt--;
}
return;
}
void tarjan(ll x){
dfn[x]=low[x]=++cnt;
S.push(x);ins[x]=1;
for(ll i=ls[x];i;i=a[i].next){
ll y=a[i].to;
if(!dfn[y]){
tarjan(y);
low[x]=min(low[x],low[y]);
}
else if(ins[y])
low[x]=min(low[x],dfn[y]);
}
if(low[x]==dfn[x]){
while(S.top()!=x){
unionn(S.top(),x);
ins[S.top()]=0;S.pop();
}
ins[x]=0;S.pop();
}
return;
}
void solve(ll l,ll r,ll L,ll R){
if(L>R)return;
if(l==r){
for(ll i=L;i<=R;i++)
e[i].T=l;
return;
}
ll mid=(l+r)>>1,zt=snt;
for(ll i=L;i<=R;i++)
if(e[i].t<=mid){
ll x=find(e[i].x),y=find(e[i].y);
addl(x,y);cl[++clt]=x;cl[++clt]=y;
}
cnt=tot=0;
for(ll i=1;i<=clt;i++)
if(!dfn[cl[i]])tarjan(cl[i]);
ll t1=0,t2=0;
for(ll i=L;i<=R;i++){
ll x=find(e[i].x),y=find(e[i].y);
if(x==y)p1[++t1]=e[i];else p2[++t2]=e[i];
}
for(ll i=1;i<=t1;i++)e[L+i-1]=p1[i];
for(ll i=1;i<=t2;i++)e[L+t1+i-1]=p2[i];
while(clt)ls[cl[clt]]=dfn[cl[clt]]=low[cl[clt]]=0,clt--;
solve(mid+1,r,L+t1,R);clearto(zt);
solve(l,mid,L,L+t1-1);
return;
}
bool cmp(enode x,enode y)
{return x.t<y.t;}
struct SegTree{
ll cnt,w[N<<5],s[N<<5],ls[N<<5],rs[N<<5];
void Change(ll &x,ll L,ll R,ll pos,ll val){
if(!x)x=++cnt;w[x]+=val;s[x]+=pos*val;
if(L==R)return;
ll mid=(L+R)>>1;
if(pos<=mid)Change(ls[x],L,mid,pos,val);
else Change(rs[x],mid+1,R,pos,val);
return;
}
ll Ask(ll x,ll L,ll R,ll k){
if(k>=w[x])return s[x];
if(L==R)return L*k;
ll mid=(L+R)>>1;
if(w[rs[x]]>=k)return Ask(rs[x],mid+1,R,k);
return s[rs[x]]+Ask(ls[x],L,mid,k-w[rs[x]]);
}
ll Merge(ll x,ll y){
if(!x||!y)return x+y;
w[x]=w[x]+w[y];s[x]=s[x]+s[y];
ls[x]=Merge(ls[x],ls[y]);
rs[x]=Merge(rs[x],rs[y]);
return x;
}
// ll Merge(ll x,ll y,ll L,ll R){
// if(!x||!y)return x+y;
// w[x]=w[x]+w[y];
// if(L==R)return x;
// ll mid=(L+R)>>1;
// ls[x]=Merge(ls[x],ls[y],L,mid);
// rs[x]=Merge(rs[x],rs[y],mid+1,R);
// return x;
// }
}T;
void Merge(ll x,ll y){
x=Find(x),y=Find(y);
if(x==y)return;
rt[x]=T.Merge(rt[x],rt[y]);
fa[y]=x;return;
}
signed main()
{
scanf("%lld%lld%lld",&n,&m,&t);
for(ll i=1;i<=n;i++)scanf("%lld",&s[i]),fa[i]=i;
for(ll i=1;i<=m;i++){
scanf("%lld%lld",&e[i].x,&e[i].y);
emp[mp(e[i].x,e[i].y)]=i;
}
for(ll i=t;i>=1;i--){
ll op,x,y;
scanf("%lld%lld%lld",&op,&x,&y);
if(op==1)e[emp[mp(x,y)]].t=i;
else if(op==2)q[i].x=-x,q[i].k=y,s[x]+=y;
else if(op==3)q[i].x=x,q[i].k=y;
}
sort(e+1,e+1+m,cmp);
solve(0,t+1,1,m);
ll z=1;
for(ll i=1;i<=n;i++)
fa[i]=i,T.Change(rt[i],1,1e9,s[i],1);
for(ll i=1;i<=t;i++){
while(z<=m&&e[z].T<=i)
Merge(e[z].x,e[z].y),z++;
if(q[i].x<0){
ll x=-q[i].x,w=q[i].k,f=Find(x);
T.Change(rt[f],1,1e9,s[x],-1);
s[x]-=w;
T.Change(rt[f],1,1e9,s[x],1);
}
else if(q[i].x>0){
ll x=Find(q[i].x),k=q[i].k;
ans[i]=T.Ask(rt[x],1,1e9,k);
}
}
for(ll i=t;i>=1;i--)
if(ans[i])printf("%lld\n",ans[i]);
return 0;
}
P5163-WD与地图【tarjan,整体二分,线段树合并】的更多相关文章
- P5163 WD与地图(整体二分+权值线段树)
传送门 细节要人命.jpg 这题思路太新奇了--首先不难发现可以倒着做变成加边,但是它还需要我们资瓷加边的同时维护强连通分量.显然加边之后暴力跑是不行的 然后有一个想法,对于一条边\((u,v)\), ...
- 【LUOGU???】WD与地图 整体二分 线段树合并
题目大意 有一个简单有向图.每个点有点权. 有三种操作: 修改点权 删除一条边 询问和某个点在同一个强连通分量中的点的前 \(k\) 大点权和. \(n\leq 100000,m,q\leq 2000 ...
- 【BZOJ-3110】K大数查询 整体二分 + 线段树
3110: [Zjoi2013]K大数查询 Time Limit: 20 Sec Memory Limit: 512 MBSubmit: 6265 Solved: 2060[Submit][Sta ...
- P5161 WD与数列(后缀自动机+线段树合并)
传送门 没想出来→_→ 首先不难看出要差分之后计算不相交也不相邻的相等子串对数,于是差分之后建SAM,在parent树上用线段树合并维护endpos集合,然后用启发式合并维护一个节点对另一个节点的贡献 ...
- (困难) CF 484E Sign on Fence,整体二分+线段树
Bizon the Champion has recently finished painting his wood fence. The fence consists of a sequence o ...
- BZOJ 3110 [ZJOI2013]K大数查询 (整体二分+线段树)
和dynamic rankings这道题的思想一样 只不过是把树状数组换成线段树区间修改,求第$K$大的而不是第$K$小的 这道题还有负数,需要离散 #include <vector> # ...
- BZOJ3110 K大数查询 【线段树 + 整体二分 或 树套树(非正解)】
Description 有N个位置,M个操作.操作有两种,每次操作如果是1 a b c的形式表示在第a个位置到第b个位置,每个位置加入一个数c 如果是2 a b c形式,表示询问从第a个位置到第b个位 ...
- P5163 WD与地图 [整体二分,强连通分量,线段树合并]
首先不用说,倒着操作.整体二分来做强连通分量,然后线段树合并,这题就做完了. // powered by c++11 // by Isaunoya #include <bits/stdc++.h ...
- 洛谷P5163 WD与地图
只有洛谷的毒瘤才会在毒瘤月赛里出毒瘤题...... 题意:三个操作,删边,改变点权,求点x所在强连通分量内前k大点权之和. 解:狗屎毒瘤数据结构乱堆...... 整体二分套(tarjan+并查集) + ...
随机推荐
- SpringBoot-AOP记录操作日志
package com.meeno.inner.oa.extend.operaterecord.aop; import com.alibaba.fastjson.JSONArray; import c ...
- IOC--框架进阶
IOC控制反转 含义:把高层对底层的依赖 转移到由第三方决定 避免高层对底层的直接依赖 使得程序架构具有良好的扩展性和稳定性 理解:就是一种目的--解除依赖 DI依赖注入 含义:在构造对象是 可以自动 ...
- redis并发锁
1.应对并发场景 避免操作数据不一致 将对redis加锁 2.考虑到异常状况无法释放锁,导致死锁 将代码块进行try-catch处理 3.考虑try时宕机依然导致死锁 对锁添加时效性,添加过期时间 4 ...
- 【springcloud】一文带你搞懂API网关
作者:aCoder2013 https://github.com/aCoder2013/blog/issues/35 前言 假设你正在开发一个电商网站,那么这里会涉及到很多后端的微服务,比如会员.商品 ...
- new[]/delete[]与new/delete区别
new[]/delete[]与new/delete完全不同-动态对象数组创建通过new[] 完成-动态对象数组的销毁通过delete[]完成-new[]/delete[]能够被重载,进而改变内存管理方 ...
- 【AE】多表的联合查询
多表的联合查询 // Create the query definition. IQueryDef queryDef = featureWorkspace.CreateQueryDef(); // P ...
- 初识apache DBCP连接池
连接案例: 首先:我们使用的是mysql数据库,所以要有一个mysql和java的JDBCjar包: 然后是DBCP中的两个jar包,DBCP使用的话,需要两个包: dbcp.jar和pool.jar ...
- kafka零拷贝机制
kafka之所以那么快,其中一个很大的原因就是零拷贝(Zero-copy)技术,零拷贝不会kafka的专利,而是操作系统的升级,又比如Netty,也用到了零拷贝. 传统IO kafka的数据是要落入磁 ...
- WebService学习总结(五)--CXF的拦截器
拦截器是Cxf的基础,Cxf中很多的功能都是由内置的拦截器来实现的,拦截器在Cxf中由Interceptor表示.拦截器的作用类似axis2中handle.Cxf的拦截器包括入拦截器和出拦截器,所有的 ...
- python 常用的文件操作命令
一.python中对文件.文件夹操作时经常用到的os模块和shutil模块常用方法. 1.得到当前工作目录,即当前Python脚本工作的目录路径: os.getcwd() 2.返回指定目录下的所有文件 ...