• 【bzoj3832】Rally


    Portal -->bzoj3832

    Description

    ​  给你一个DAG,每条边长度都是(1),请找一个点满足删掉这个点之后剩余图中的最长路最短

    Solution

    ​​  这题的话感觉思路挺妙的

    ​​  因为要涉及到删点操作,所以我们肯定不能通过直接的方法来算最长路,然后因为这题没有固定的起点和终点并且是一个DAG,所以有一个比较简单粗暴的处理方法就是像网络流一样建一个超级源(S)和一个超级汇(T),每个点都从(S)连一条长度为(0)的边,每个点都向(T)连一条长度为(0)的边

    ​  我们记(f[i])表示从(S)(i)点的最长路,记(g[i])表示从(i)点到(T)点的最长路,这两个东西都可以按拓扑序转移直接dp出来(正反各来一次就好了),那么我们考虑一条边((u,v))的贡献(也就是钦定要经过这条边的最长路)为(f[u]+1+g[v]),那么最长路的问题就变成了求边贡献的最大值

    ​​  这个时候,考虑(S)(T)的一个割(注意:这里不一定是最小割!只是一个普通的割即可),那么根据其定义,所有从(S)(T)的路径必定经过割集中的最少一条边,也就是说,如果我们将(f[u]+1+g[v])看成原图中((u,v))这条边的边权,将((S,i))的边权看成(f[i])((i,T))的边权看成(g[i]),割集中边权的最大值便是我们需要的答案

    ​​  

    ​​  那么现在我们只要知道删掉一个点之后,剩余图中(S)(T)的一个割就可以知道删掉这个点的答案了,至此我们已经完成了问题的转化,现在的问题是我们怎么维护这个割集

    ​​  我们考虑用一个数据结构来快速获得边权最大值(线段树或者堆都ok)

    ​​  假设一开始我们先把所有的((i,T))的边全部断掉(这显然是一个可行的割),也就是先将所有的(g[i])丢进数据结构里面,现在考虑删掉一个点(x)的影响:所有拓扑序在(x)之后的点的(f)值可能会被影响,所有拓扑序在(x)之前的点的(g)值可能会被影响,这个时候为了保证正确性我们应该将当前割集中可能会被改变边权的边全部删掉

    ​​  

    ​​  但是这样怎么保证当前最大值一定在数据结构里面呢?这里需要一点关于操作顺序的技巧,下面先将过程讲一下:

    ​  我们考虑按照拓扑序从小到大依次计算删除这个点之后的最长路,对于当前枚举到的点(x),我们进行如下操作:

    1、将((x,T))这条边删掉,将所有的((u,x))删掉(如果本来就没有就不进行操作)

    2、统计当前割集中边权的最大值,更新答案

    3、将((S,x))这条边加入割集,将所有的((x,v))加入割集

    ​​  具体一点的话就是,首先(1)操作就是将割集中所有与(x)有关的边全部删掉,(2)不用说,关键是(3)操作,为什么我们还原割集的时候不是将(1)操作中删除的边加回进去呢?

    ​​  因为我们是按照拓扑序从小到大删点的,根据我们前面分析出来的删点影响,在删除后面的点的时候,(g[x])是可能会被影响的值(转移过来的边可能会被删掉),因此为了保证正确性我们不能将其留在割集中,同时为了保证这是一个割集,相对应的边权不会被影响到并且效果类似的((S,x))就应该被加入割集,同样的道理((u,x))的边权中的(g[x])可能会被影响所以要删掉,然后为了保证一定是一个割集所以要加入((x,v))这类边

    ​​  

    ​​  那么会不会出现有的边的贡献作为答案没有被统计的情况呢?这个。。是不会的因为如果说((u,x))这类边的贡献是答案的话,一定会在前面被计算到

    ​​  然后这题就十分愉快地做完啦ovo时间复杂度(O(nlogn))

    ​  

    ​​  代码大概长这个样子

    #include<iostream>
    #include<cstdio>
    #include<queue>
    #include<cstring>
    using namespace std;
    const int N=500010,M=1000010,SEG=N*4,inf=2147483647;
    struct xxx{
    	int y,nxt;
    }a[M*2];
    queue<int> q;
    int h1[N],h[N],in[N],out[N],f[N],g[N];
    int rec[N];
    int n,m,tot,ans,ansnode;
    namespace Seg{/*{{{*/
    	int ch[SEG][2],mx[SEG],cnt[SEG];
    	int n,tot;
    	void pushup(int x){mx[x]=max(mx[ch[x][0]],mx[ch[x][1]]);}
    	void _build(int x,int l,int r){
    		mx[x]=-1;
    		if (l==r) return;
    		int mid=l+r>>1;
    		ch[x][0]=++tot; _build(ch[x][0],l,mid);
    		ch[x][1]=++tot; _build(ch[x][1],mid+1,r);
    	}
    	void build(int _n){tot=1; n=_n; _build(1,1,n);}
    	void _insert(int x,int d,int lx,int rx,int delta){
    		if (lx==rx){
    			cnt[x]+=delta;
    			if (cnt[x]>0) mx[x]=lx;
    			else cnt[x]=0,mx[x]=-1;
    			return;
    		}
    		int mid=lx+rx>>1;
    		if (d<=mid) _insert(ch[x][0],d,lx,mid,delta);
    		else _insert(ch[x][1],d,mid+1,rx,delta);
    		pushup(x);
    	}
    	void insert(int d,int delta){_insert(1,d,1,n,delta);}
    	int query(){return mx[1];}
    }/*}}}*/
    void add(int x,int y){
    	a[++tot].y=y; a[tot].nxt=h[x]; h[x]=tot;
    	a[++tot].y=x; a[tot].nxt=h1[y]; h1[y]=tot;
    }
    void prework(){
    	int u,v;
    	while (!q.empty()) q.pop();
    	for (int i=1;i<=n;++i)
    		if (!in[i]) q.push(i),f[i]=0;
    	rec[0]=0;
    	while (!q.empty()){
    		v=q.front(); q.pop(); rec[++rec[0]]=v;
    		for (int i=h[v];i!=-1;i=a[i].nxt){
    			u=a[i].y;
    			f[u]=max(f[u],f[v]+1);
    			--in[u];
    			if (!in[u]) q.push(u);
    		}
    	}
    	for (int i=1;i<=n;++i)
    		if (!out[i]) q.push(i),g[i]=0;
    	while (!q.empty()){
    		v=q.front(); q.pop();
    		for (int i=h1[v];i!=-1;i=a[i].nxt){
    			u=a[i].y;
    			g[u]=max(g[u],g[v]+1);
    			--out[u];
    			if (!out[u]) q.push(u);
    		}
    	}
    }
    void solve(){
    	int x,u,tmp;
    	ans=inf; ansnode=0;
    	Seg::build(n+1);
    	for (int i=1;i<=n;++i) Seg::insert(g[i]+1,1);
    	for (int i=1;i<=n;++i){
    		x=rec[i];
    		Seg::insert(g[x]+1,-1);
    		for (int j=h1[x];j!=-1;j=a[j].nxt){
    			u=a[j].y;
    			Seg::insert(f[u]+1+g[x]+1,-1);
    		}
    
    		tmp=Seg::query();
    		if (ans>tmp)
    			ans=tmp,ansnode=x;
    
    		Seg::insert(f[x]+1,1);
    		for (int j=h[x];j!=-1;j=a[j].nxt){
    			u=a[j].y;
    			Seg::insert(f[x]+1+g[u]+1,1);
    		}
    	}
    	printf("%d %d
    ",ansnode,ans-1);
    }
    
    int main(){
    #ifndef ONLINE_JUDGE
    	freopen("a.in","r",stdin);
    #endif
    	int x,y;
    	scanf("%d%d",&n,&m);
    	memset(h,-1,sizeof(h));
    	memset(h1,-1,sizeof(h1));
    	tot=0;
    	for (int i=1;i<=m;++i){
    		scanf("%d%d",&x,&y);
    		add(x,y);
    		++in[y]; ++out[x];
    	}
    	prework();
    	solve();
    }
    
  • 相关阅读:
    【转】直方图中bins的理解及处理
    [LeetCode] 1. Two Sum
    数据结构课学到的常用知识总结概括
    Java核心技术第三章数据类型
    mysql 出现You can't specify target table for update in FROM clause错误的解决方法
    mysql 从一个表中查数据并插入另一个表实现方法
    使用ECharts,绘制柱状图
    mysql 查询1小时内_mysql查询一个小时之内的数据
    mysql查询表中所有字段的名字
    MySQL 时间函数总集
  • 原文地址:https://www.cnblogs.com/yoyoball/p/9544353.html
Copyright © 2020-2023  润新知