ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

[luogu 5163] WD与地图

2022-03-02 16:05:01  阅读:206  来源: 互联网

标签:WD return rs int luogu top mid num 5163


一、题目

点此看题

二、解法

只能说是精神污染了,虽然每个部分都不难把但是放在一起就很难写了。

考虑无向图的情况是好做的,我们直接离线逆序询问,那么删边操作就变成了加边,单点增加操作就变成了单点减少。那么做法是显然的,我们线段树合并维护加边操作,再支持线段树单点修改和线段树上二分即可。

本题是强连通分量怎么办呢?我们类比无向图的情况,还是先离线逆序询问,考虑把每一条边的效果等效为合并两个强连通块。那么我们求出每一条边有用的时间 \(f\),也就是恰好在这个时刻这条边的两个端点在同一个强连通分量里

发现这个时刻是单调的,而我们要对所有边都求出这样一个时刻,那么可以考虑整体二分。考虑现在要判断一条边的 \(f\) 和 \(mid\) 的大小关系,那么我们保留加入时间在 \([0,mid]\) 的边(为 \(0\) 就表示没有被删除),如果这条边的加入时间 \(\leq mid\) 并且两端点在同一强连通分量中,那么它的 \(f\leq mid\),否则 \(f>mid\)

但是这样做时间复杂度又有点不对,我们可以考虑动态 \(\tt tarjan\),也就是假设我们已经对保留 \([0,l)\) 边的图缩过点了,那么我们再此基础上加入 \([l,mid]\) 的边就可以了。所以我们整体二分的时候先走右边再走左边,走右边的时候把新加入的边也保留,然后使用可撤销并查集就可以维护缩点的结果。

时间复杂度 \(O(n\log^2 n)\),复杂度瓶颈在可撤销并查集。

#include <cstdio>
#include <vector>
#include <iostream>
#include <algorithm>
#include <stack>
#include <map>
using namespace std;
const int M = 200005;
const int N = 30*M;
#define ll long long
const int up = 1e9;
int read()
{
	int x=0,f=1;char c;
	while((c=getchar())<'0' || c>'9') {if(c=='-') f=-1;}
	while(c>='0' && c<='9') {x=(x<<3)+(x<<1)+(c^48);c=getchar();}
	return x*f;
}
int n,m,k,t,c[M],fa[M],siz[M],top,a[M],b[M],p[M<<1];
int cnt,rt[M],ls[N],rs[N],num[N];ll ans[M],sum[N];
int Ind,low[M],dfn[M],in[M];stack<int> s;
vector<int> g[M];map<ll,int> mp;
struct node
{
	int t,x,y;
	bool operator < (const node &r) const
		{return t<r.t;}
}q[M],e[M],el[M],er[M],d[M];
//namespace : divide and conquer
int find(int x)
{
	return fa[x]==x?x:find(fa[x]);
}
void merge(int u,int v)
{
	int x=find(u),y=find(v);
	if(x==y) return ;
	if(siz[x]<siz[y]) swap(x,y);
	//guarantee that siz[x]>siz[y]
	siz[x]+=siz[y];fa[y]=x;
	top++;a[top]=x;b[top]=y;
}
void tarjan(int u)
{
	low[u]=dfn[u]=++Ind;
	s.push(u);in[u]=1;
	for(int v:g[u])
	{
		if(!dfn[v])
		{
			tarjan(v);
			low[u]=min(low[u],low[v]);
		}
		else if(in[v])
			low[u]=min(low[u],dfn[v]);
	}
	if(low[u]==dfn[u])
	{
		int v=0;
		do
		{
			v=s.top();s.pop();
			merge(v,u);in[v]=0;
		}while(v!=u);
	}
}
void popout()
{
	int u=a[top],v=b[top--];
	siz[u]-=siz[v];fa[v]=v;
}
void cdq(int l,int r,int L,int R)
{
	if(l>r) return ;
	if(L==R)
	{
		for(int i=l;i<=r;i++)
			d[++t]=node{L,e[i].x,e[i].y};
		return ;
	}
	int mid=(L+R)>>1,tl=0,tr=0,num=0,now=top;
	//build the graph of [L,mid]
	for(int i=l;i<=r;i++)
	{
		if(e[i].t>mid) continue;
		int x=find(e[i].x),y=find(e[i].y);
		p[++num]=x;p[++num]=y;g[x].push_back(y);
	}
	//do partial tarjan
	for(int i=1;i<=num;i++)
		if(!dfn[p[i]]) tarjan(p[i]);
	//partition [l,r]
	for(int i=l;i<=r;i++)
	{
		if(e[i].t<=mid && find(e[i].x)==find(e[i].y))
			el[++tl]=e[i];
		else er[++tr]=e[i];
	}
	for(int i=1;i<=tl;i++) e[l+i-1]=el[i];
	for(int i=1;i<=tr;i++) e[l+tl+i-1]=er[i];
	//clear
	for(int i=1;i<=num;i++)
		low[p[i]]=dfn[p[i]]=0,g[p[i]].clear();
	Ind=num=0;
	//right first !!!
	cdq(l+tl,r,mid+1,R);
	while(top>now) popout();
	cdq(l,l+tl-1,L,mid);
}
//namespace : segment tree
int Find(int x)
{
	if(x!=fa[x]) fa[x]=Find(fa[x]);
	return fa[x];
}
void ins(int &x,int l,int r,int id,int f)
{
	if(!x) x=++cnt;
	sum[x]+=f*id;num[x]+=f;
	if(l==r) return ;
	int mid=(l+r)>>1;
	if(mid>=id) ins(ls[x],l,mid,id,f);
	else ins(rs[x],mid+1,r,id,f);
}
int comb(int x,int y)
{
	if(!x || !y) return x+y;
	sum[x]+=sum[y];num[x]+=num[y];
	ls[x]=comb(ls[x],ls[y]);
	rs[x]=comb(rs[x],rs[y]);
	return x;
}
ll ask(int x,int l,int r,int k)
{
	if(num[x]<=k) return sum[x];
	if(!x) return 0;
	if(l==r) return 1ll*min(num[x],k)*l;
	int mid=(l+r)>>1;
	if(num[rs[x]]>=k) return ask(rs[x],mid+1,r,k);
	return ask(ls[x],l,mid,k-num[rs[x]])+sum[rs[x]];
}
signed main()
{
	//initialize
	n=read();m=read();k=read();
	for(int i=1;i<=n;i++)
		c[i]=read(),fa[i]=i,siz[i]=1;
	for(int i=1;i<=m;i++)
	{
		int u=read(),v=read();
		e[i].x=u;e[i].y=v;mp[1ll*u*n+v]=i;
	}
	for(int i=1;i<=k;i++)
	{
		q[i].t=read(),q[i].x=read(),q[i].y=read();
		if(q[i].t==2) c[q[i].x]+=q[i].y;
	}
	//reverse the queries
	reverse(q+1,q+1+k);
	//calc the time in queries and go to cdq
	for(int i=1;i<=k;i++) if(q[i].t==1)
		e[mp[1ll*q[i].x*n+q[i].y]].t=i;
	cdq(1,m,0,k+1);
	//calc the answer as undirected graph
	sort(d+1,d+1+t);
	for(int i=1;i<=n;i++)
		fa[i]=i,ins(rt[i],0,up,c[i],1);
	for(int i=1,j=1;i<=k;i++)
	{
		//link the edge
		while(j<=t && d[j].t<=i)
		{
			int x=Find(d[j].x),y=Find(d[j].y);j++;
			if(x==y) continue ;
			fa[y]=x;rt[x]=comb(rt[x],rt[y]);
		}
		int x=q[i].x,y=q[i].y;ans[i]=-1;
		if(q[i].t==2)
		{
			ins(rt[Find(x)],0,up,c[x],-1);
			c[x]-=y;
			ins(rt[Find(x)],0,up,c[x],1);
		}
		if(q[i].t==3)
			ans[i]=ask(rt[Find(x)],0,up,y);
	}
	for(int i=k;i>=1;i--) if(ans[i]!=-1)
		printf("%lld\n",ans[i]);
}

标签:WD,return,rs,int,luogu,top,mid,num,5163
来源: https://www.cnblogs.com/C202044zxy/p/15955558.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有