P5163 WD与地图(整体二分+权值线段树)
细节要人命.jpg
这题思路太新奇了……首先不难发现可以倒着做变成加边,但是它还需要我们资瓷加边的同时维护强连通分量。显然加边之后暴力跑是不行的
然后有一个想法,对于一条边\((u,v)\),如果所有加入时间在\((0,t)\)之间的边能够使\(u,v\)在同一个强连通分量里,那么\((u,v)\)这条边的成立时间就是最小的满足条件的\(t\)。
对于每一个强联通分量维护一棵权值线段树,修改的话就是一个权值出现次数\(+1\),另一个\(-1\),找前\(k\)大的总和可以类似于主席树的那种二分,强联通分量的合并可以线段树合并。
于是就可以枚举时间\(t\),对于每一条成立时间为\(t\)的边\((u,v)\),我们用并查集把它们连起来,再把他们对应的强连通分量的权值线段树合并起来,这样就查询和修改就直接在权值线段树上进行即可
现在的问题是如何求出每条边的成立时间。这个可以整体二分。每次二分一个\(mid\),把所有加入时间小于等于\(mid\)的边加入图中跑一遍\(tarjan\),就可以知道哪些边的成立时间小于等于\(mid\),然后继续二分下去就好了。然后可以用一个什么东西来维护当前并查集的情况,这样左边做完之后就不用再做一次,可以直接进右边做了
注意细节
//minamoto
#include<bits/stdc++.h>
#define R register
#define ll long long
#define pi pair<int,int>
#define IT vector<pi>::iterator
#define fp(i,a,b) for(R int i=a,I=b+1;i<I;++i)
#define fd(i,a,b) for(R int i=a,I=b-1;i>I;--i)
#define go(u) for(int i=head[u],v=e[i].v;i;i=e[i].nx,v=e[i].v)
#define gg(mp) for(IT it=mp.begin();it!=mp.end();++it)
template<class T>inline bool cmin(T&a,const T&b){return a>b?a=b,1:0;}
template<class T>inline bool cmax(T&a,const T&b){return a<b?a=b,1:0;}
using namespace std;
char buf[1<<21],*p1=buf,*p2=buf;
inline char getc(){return p1==p2&&(p2=(p1=buf)+fread(buf,1,1<<21,stdin),p1==p2)?EOF:*p1++;}
int read(){
R int res,f=1;R char ch;
while((ch=getc())>'9'||ch<'0')(ch=='-')&&(f=-1);
for(res=ch-'0';(ch=getc())>='0'&&ch<='9';res=res*10+ch-'0');
return res*f;
}
char sr[1<<21],z[20];int C=-1,Z=0;
inline void Ot(){fwrite(sr,1,C+1,stdout),C=-1;}
void print(R ll x){
if(C>1<<20)Ot();if(x<0)sr[++C]='-',x=-x;
while(z[++Z]=x%10+48,x/=10);
while(sr[++C]=z[Z],--Z);sr[++C]='\n';
}
const int N=1e6+5;
struct eg{int v,nx;}e[N];int head[N],tot;
struct node{int u,v,t;}a[N],b[N];vector<node>path[N];
struct point{int ls,rs,s;ll sum;}t[N<<5];ll ans[N];set<pi>s;
int fa[N],rt[N],low[N],dfn[N],st[N],ins[N],q[N],type[N],aa[N],bb[N],val[N];
int n,m,tim,qaq,top,cnt,h,u,v,lim;
int find(int x){return fa[x]==x?x:fa[x]=find(fa[x]);}
void unite(R int x,R int y){
x=find(x),y=find(y);if(x==y)return;
fa[x]=y;
}
void tarjan(int u){
dfn[u]=low[u]=++tim,st[++top]=u,ins[u]=1;
go(u)if(!dfn[v])tarjan(v),cmin(low[u],low[v]);
else if(ins[v])cmin(low[u],dfn[v]);
if(low[u]==dfn[u])do{ins[st[top]]=0,unite(st[top],u);}while(st[top--]!=u);
}
void update(int &p,int l,int r,int k,int x){
// printf("%d %d\n",l,r);
if(!p)p=++cnt;t[p].s+=k,t[p].sum+=x*k;if(l==r)return;
int mid=(l+r)>>1;
x<=mid?update(t[p].ls,l,mid,k,x):update(t[p].rs,mid+1,r,k,x);
}
ll query(int p,int l,int r,int k){
if(k>=t[p].s)return t[p].sum;
if(l==r)return t[p].sum/t[p].s*k;
int mid=(l+r)>>1;
if(t[t[p].rs].s>=k)return query(t[p].rs,mid+1,r,k);
return query(t[p].ls,l,mid,k-t[t[p].rs].s)+t[t[p].rs].sum;
}
int merge(int x,int y){
if(!x||!y)return x|y;
t[x].s+=t[y].s,t[x].sum+=t[y].sum;
t[x].ls=merge(t[x].ls,t[y].ls);
t[x].rs=merge(t[x].rs,t[y].rs);
return x;
}
void solve(int l,int r,int ql,int qr){
// printf("%d %d %d %d\n",l,r,ql,qr);
if(l==r){
fp(i,ql,qr)path[l].push_back(a[i]);
return;
}if(ql==qr){
if(find(a[ql].u)==find(a[ql].v))
path[a[ql].t].push_back(a[ql]);
return;
}int mid=(l+r)>>1;tot=h=0;
fp(i,ql,qr)if(a[i].t<=mid){
int u=find(a[i].u),v=find(a[i].v);
q[++h]=u,q[++h]=v,head[u]=head[v]=0;
}for(R int i=1;i<=h;i+=2){
int u=q[i],v=q[i+1];
e[++tot]={v,head[u]},head[u]=tot;
dfn[u]=low[u]=ins[u]=0;
dfn[v]=low[v]=ins[v]=0;
}top=tim=0;
fp(i,1,h)if(!dfn[q[i]])tarjan(q[i]);
vector<pi>mp;int t1=0,t2=ql;
h=1;
fp(i,ql,qr){
int flag=0;
if(a[i].t<=mid){
int u=q[h],v=q[h+1];h+=2;
if(find(u)==find(v))flag=1;
mp.push_back(pi(u,fa[u]));
mp.push_back(pi(v,fa[v]));
}if(flag)a[t2++]=a[i];
else b[++t1]=a[i];
}fp(i,t2,qr)a[i]=b[i-t2+1];
fp(i,1,h-1)fa[q[i]]=q[i];
// printf("%d %d\n",t1,t2);
if(ql<t2)solve(l,mid,ql,t2-1);
gg(mp)fa[it->first]=it->second;
if(qr>=t2)solve(mid+1,r,t2,qr);
}
int main(){
// freopen("testdata.in","r",stdin);
// freopen("testdata.out","w",stdout);
n=read(),m=read(),qaq=read();
fp(i,1,n)val[i]=read(),fa[i]=i;
fp(i,1,m)u=read(),v=read(),s.insert(pi(u,v));
fd(i,qaq,1){
int op=read(),u=read(),v=read();
type[i]=op,aa[i]=u,bb[i]=v;
if(op==1)a[++tot]={u,v,i},s.erase(pi(u,v));
else if(op==2)val[u]+=v;
}for(set<pi>::iterator it=s.begin();it!=s.end();++it)a[++tot]={it->first,it->second,0};
solve(0,qaq+1,1,m);
tot=top=0,type[0]=1;
fp(i,1,n)cmax(lim,val[i]),fa[i]=i;
fp(i,1,n)update(rt[i],1,lim,1,val[i]);
// puts("QAQ");
fp(i,0,qaq)if(type[i]==1){
for(vector<node>::iterator it=path[i].begin();it!=path[i].end();++it){
it->u=find(it->u),it->v=find(it->v);
if(it->u==it->v)continue;
fa[it->v]=it->u;
rt[it->u]=merge(rt[it->u],rt[it->v]);
}
}else if(type[i]==2){
int u=find(aa[i]);update(rt[u],1,lim,-1,val[aa[i]]);
val[aa[i]]-=bb[i];update(rt[u],1,lim,1,val[aa[i]]);
}else ans[++top]=query(rt[find(aa[i])],1,lim,bb[i]);
while(top)print(ans[top--]);
return Ot(),0;
}
P5163 WD与地图(整体二分+权值线段树)的更多相关文章
- 2019.01.14 bzoj5343: [Ctsc2018]混合果汁(整体二分+权值线段树)
传送门 整体二分好题. 题意简述:nnn种果汁,每种有三个属性:美味度,单位体积价格,购买体积上限. 现在有mmm个询问,每次问能否混合出总体积大于某个值,总价格小于某个值的果汁,如果能,求所有方案中 ...
- HDU6621 K-th Closest Distance 第 k 小绝对值(主席树(统计范围的数有多少个)+ 二分 || 权值线段树+二分)
题意:给一个数组,每次给 l ,r, p, k,问区间 [l, r] 的数与 p 作差的绝对值的第 k 小,这个绝对值是多少 分析:首先我们先分析单次查询怎么做: 题目给出的数据与多次查询已经在提示着 ...
- P5163 WD与地图 [整体二分,强连通分量,线段树合并]
首先不用说,倒着操作.整体二分来做强连通分量,然后线段树合并,这题就做完了. // powered by c++11 // by Isaunoya #include <bits/stdc++.h ...
- The Stream of Corning 2( 权值线段树/(树状数组+二分) )
题意: 有两种操作:1.在[l,r]上插入一条值为val的线段 2.问p位置上值第k小的线段的值(是否存在) 特别的,询问的时候l和p合起来是一个递增序列 1<=l,r<=1e9:1< ...
- 2019杭电多校第三场hdu6606 Distribution of books(二分答案+dp+权值线段树)
Distribution of books 题目传送门 解题思路 求最大值的最小值,可以想到用二分答案. 对于二分出的每个mid,要找到是否存在前缀可以份为小于等于mid的k份.先求出这n个数的前缀和 ...
- 【bzoj3110】[Zjoi2013]K大数查询 权值线段树套区间线段树
题目描述 有N个位置,M个操作.操作有两种,每次操作如果是1 a b c的形式表示在第a个位置到第b个位置,每个位置加入一个数c.如果是2 a b c形式,表示询问从第a个位置到第b个位置,第C大的数 ...
- P1486 [NOI2004]郁闷的出纳员[权值线段树]
权值线段树. 我们只用维护一个人是否存在,以及他当前排名,而不关心工资的具体值,这个可以直接算. 不难发现,如果不考虑新的员工,所有员工的工资的差值是不变的. 而加进来一个新的员工时,其工资为\(x\ ...
- 线段树(单标记+离散化+扫描线+双标记)+zkw线段树+权值线段树+主席树及一些例题
“队列进出图上的方向 线段树区间修改求出总量 可持久留下的迹象 我们 俯身欣赏” ----<膜你抄> 线段树很早就会写了,但一直没有总结,所以偶尔重写又会懵逼,所以还是要总结一下. ...
- BZOJ_2161_布娃娃_权值线段树
BZOJ_2161_布娃娃_权值线段树 Description 小时候的雨荨非常听话,是父母眼中的好孩子.在学校是老师的左右手,同学的好榜样.后来她成为艾利斯顿第二 代考神,这和小时候培养的良好素质是 ...
随机推荐
- 传奇的诞生,PHP三位创始人简介
PHP到现在为止已经诞生12年了.在这期间它经过不断改善,已经成为Web开发最重要的语言之一.PHP能有今天这样的成就,它的3位创始人(Rasmus Lerdorf.Zeev Suraski和Andi ...
- 【翻译自mos文章】多租户中的service管理
来源于: Service Management For Multitenant (文档 ID 2009500.1) APPLIES TO: Oracle Database - Enterprise E ...
- 下载Google官方/CM Android源码自己主动又一次開始的Shell脚本
国内因为某种原因,下载CM或Google官方的Android源码总easy中断.总看着机器.一中断就又一次运行repo sync还太麻烦,所以我特意编写了一段shell脚本(download.sh). ...
- Java核心技术之基本数据类型
这篇文章.我们讨论一些java的最主要的东西.这些东西我们一般刚刚学java的时候就学过,可是不一定真正明确. 正好,我在做一个读取内存的值,涉及到bit位的值的读取和写.那就能够讨论一个java的基 ...
- stm8 停机模式与外部中断唤醒中一个小问题
做了一个简单的项目,电路板使用电池供电,需要系统在待机时低功耗.而对外接口只有4个按键,也就是唤醒必须要通过这四个按键. 系统功能就不介绍了,只给出进入低功耗的代码和退出低功耗的代码. 使用芯片为st ...
- Why you shouldn’t connect your mobile application to a database
BY CRAIG CHAPMAN · PUBLISHED 2015-07-02 · UPDATED 2015-07-02 Working at Embarcadero, I frequently ...
- p1199八数码问题
oj上简化的八数码问题,最强的数据仅仅是20步: 根据曼哈顿距离构造启发函数: 主算法:IDA*:(使用方法好像不太对......) 未用位运算优化: #include<iostream> ...
- mpvue——实现点击数组内的某一元素进行置顶(排序第一)操作
前言 其实很简单只是用了js的几个函数 substr unshift splice 完整代码 | mpvue模仿QQ 代码 思路很简单,获取当前元素下标然后通过unshift函数将该值插入到数组第一位 ...
- MYSQL进阶学习笔记二:MySQL存储过程和局部变量!(视频序号:进阶_4-6)
知识点三:MySQL存储过程和局部变量(4,5,6) 存储过程的创建: 创建存储过程的步骤: 首先选中数据库 改变分隔符,不让分号作为执行结束的标记.(通常情况下,改变分隔符命令 DELIMI ...
- (转)Linux下 SVN客户端安装
原地址:http://rtxbc.iteye.com/blog/860092 今天有现场程序连svn服务器一直有异常,于是在现场linux下安装svn client来直接测试,看问题原因: 一:安装s ...