传送门

细节要人命.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与地图(整体二分+权值线段树)的更多相关文章

  1. 2019.01.14 bzoj5343: [Ctsc2018]混合果汁(整体二分+权值线段树)

    传送门 整体二分好题. 题意简述:nnn种果汁,每种有三个属性:美味度,单位体积价格,购买体积上限. 现在有mmm个询问,每次问能否混合出总体积大于某个值,总价格小于某个值的果汁,如果能,求所有方案中 ...

  2. HDU6621 K-th Closest Distance 第 k 小绝对值(主席树(统计范围的数有多少个)+ 二分 || 权值线段树+二分)

    题意:给一个数组,每次给 l ,r, p, k,问区间 [l, r] 的数与 p 作差的绝对值的第 k 小,这个绝对值是多少 分析:首先我们先分析单次查询怎么做: 题目给出的数据与多次查询已经在提示着 ...

  3. P5163 WD与地图 [整体二分,强连通分量,线段树合并]

    首先不用说,倒着操作.整体二分来做强连通分量,然后线段树合并,这题就做完了. // powered by c++11 // by Isaunoya #include <bits/stdc++.h ...

  4. The Stream of Corning 2( 权值线段树/(树状数组+二分) )

    题意: 有两种操作:1.在[l,r]上插入一条值为val的线段 2.问p位置上值第k小的线段的值(是否存在) 特别的,询问的时候l和p合起来是一个递增序列 1<=l,r<=1e9:1< ...

  5. 2019杭电多校第三场hdu6606 Distribution of books(二分答案+dp+权值线段树)

    Distribution of books 题目传送门 解题思路 求最大值的最小值,可以想到用二分答案. 对于二分出的每个mid,要找到是否存在前缀可以份为小于等于mid的k份.先求出这n个数的前缀和 ...

  6. 【bzoj3110】[Zjoi2013]K大数查询 权值线段树套区间线段树

    题目描述 有N个位置,M个操作.操作有两种,每次操作如果是1 a b c的形式表示在第a个位置到第b个位置,每个位置加入一个数c.如果是2 a b c形式,表示询问从第a个位置到第b个位置,第C大的数 ...

  7. P1486 [NOI2004]郁闷的出纳员[权值线段树]

    权值线段树. 我们只用维护一个人是否存在,以及他当前排名,而不关心工资的具体值,这个可以直接算. 不难发现,如果不考虑新的员工,所有员工的工资的差值是不变的. 而加进来一个新的员工时,其工资为\(x\ ...

  8. 线段树(单标记+离散化+扫描线+双标记)+zkw线段树+权值线段树+主席树及一些例题

    “队列进出图上的方向 线段树区间修改求出总量 可持久留下的迹象 我们 俯身欣赏” ----<膜你抄>     线段树很早就会写了,但一直没有总结,所以偶尔重写又会懵逼,所以还是要总结一下. ...

  9. BZOJ_2161_布娃娃_权值线段树

    BZOJ_2161_布娃娃_权值线段树 Description 小时候的雨荨非常听话,是父母眼中的好孩子.在学校是老师的左右手,同学的好榜样.后来她成为艾利斯顿第二 代考神,这和小时候培养的良好素质是 ...

随机推荐

  1. 编译 Deedle

    编译 Deedle Deedle 中含有 RProvider. 要编译 Deedle.须要先下载 R.地址: http://cran.cnr.berkeley.edu/bin/windows/base ...

  2. Json的简单介绍和解析

    Json:JavaScript对象表示法(JavaScript Object Noatation) Json是存储和交换文本信息的语法,类似XML.它采用键值对的方式来组织,易于人们阅读和编写,同时也 ...

  3. Android_Service详解及实例

    转自:http://blog.csdn.net/guolin_blog/article/details/11952435    http://blog.csdn.net/guolin_blog/art ...

  4. NPOI实现Excel导入

    导入功能实现: ]; GetExtensionsFromFileStream(file.InputStream); using NPOI.XSSF.UserModel; public List< ...

  5. Qt5官方demo解析集13——Qt Quick Particles Examples - Image Particles

    本系列全部文章能够在这里查看http://blog.csdn.net/cloud_castle/article/category/2123873 接上文 Qt5官方demo解析集12--Qt Quic ...

  6. HBase运维基础--元数据逆向修复原理

    背景 鉴于上次一篇文章——“云HBase小组成功抢救某公司自建HBase集群,挽救30+T数据”的读者反馈,对HBase的逆向工程比较感兴趣,并咨询如何使用相应工具进行运维等等.总的来说,就是想更深层 ...

  7. CrateDb

    CrateDB: Real-time SQL Database for Machine Data & IoT | Crate.io https://crate.io/

  8. JOptionPane常用提示框

    //JOptionPane.showMessageDialog(parentComponent, message, title, messageType, icon); JOptionPane.sho ...

  9. Codeforces Beta Round #25 (Div. 2 Only)D. Roads not only in Berland

    D. Roads not only in Berland time limit per test 2 seconds memory limit per test 256 megabytes input ...

  10. Windows下VMware虚拟机使用Centos,Docker方式安装openstf的小坑

    今天使用docker方式安装openstf碰到了一小坑,坑了我半天.特此记录! docker方式安装stf就不说了,网上教程一大把. 但是... 安装完之后.进入web控制界面,手机连接的好好的.但硕 ...