zoukankan      html  css  js  c++  java
  • [bzoj3832]Rally——拓扑排序+堆

    题目大意:

    给定一个N个点M条边的有向无环图,每条边长度都是1。
    请找到一个点,使得删掉这个点后剩余的图中的最长路径最短。

    思路:

    首先我们加一个超级源S和一个超级汇T,然后整个题目就变成了求(S->T)的最长链。计算出S到每一个点的最长路和每一个点到T的最长路,这样我们就可以很方便地算出来经过任意一条边的最长路了。
    考虑到删除一个点之后新的最长链一定会经过一些边,于是我们可以维护一个集合代表删除了这个点之后新的最长路可能会经过哪些边。
    假设我们删除了点u。
    首先我们所维护的集合内的边一定和点u没有偏序关系,否则经过这条边的最长路可能会经过点u。
    其次有偏序关系的边也没有必要重复放入集合中。
    于是一个集合我们可以看成是一个割,其中的边都是平行的,不难发现图中的所有路径都经过了这个割中的边。
    删除一个点之后我们将这个割中和这个点相连的所有边去掉,剩下的边集的答案便一定是去掉了这个点之后的最长路的长度。
    于是接下来就只需要动态维护这个割集并使它满足上面的条件,按照拓扑序的过程,维护一个指向目前的点集的边集,每次选定一个点将所有指向它的边删除,这个集合便一定满足性质,计算完之后再将u删除,将所有从u出发的边加入集合即可。
    上述的过程可以用权值线段树或堆来实现。

    #include<bits/stdc++.h>
    
    #define REP(i,a,b) for(int i=a,i##_end_=b;i<=i##_end_;++i)
    #define pii pair<int,int>
    #define fi first
    #define se second
    #define mk make_pair
    #define pb push_back
    typedef long long ll;
    
    using namespace std;
    
    void File(){
    	freopen("bzoj3832.in","r",stdin);
    	freopen("bzoj3832.out","w",stdout);
    }
    
    template<typename T>void read(T &_){
    	T __=0,mul=1; char ch=getchar();
    	while(!isdigit(ch)){
    		if(ch=='-')mul=-1;
    		ch=getchar();
    	}
    	while(isdigit(ch))__=(__<<1)+(__<<3)+(ch^'0'),ch=getchar();
    	_=__*mul;
    }
    
    const int maxn=5e5+10;
    const int maxm=2e6+10;
    const int inf=0x3f3f3f3f;
    int n,m,f[maxn],g[maxn];
    int beg[maxn],las[maxm],to[maxm],from[maxm],cnte=1;
    
    void add(int u,int v){
    	las[++cnte]=beg[u]; beg[u]=cnte; to[cnte]=v; from[cnte]=u;
    }
    
    namespace dp{
    	void get_f(){
    		int deg[maxn]={0};
    		queue<int>qu;
    		REP(i,2,cnte)++deg[to[i]];
    		REP(i,0,n+1)if(!deg[i])qu.push(i),f[i]=0;
    		while(!qu.empty()){
    			int u=qu.front(); qu.pop();
    			for(int i=beg[u];i;i=las[i]){
    				 int v=to[i]; --deg[v];
    				 f[v]=max(f[v],f[u])+1;
    				 if(!deg[v])qu.push(v);
    			}
    		}
    	}
    	void get_g(int u){
    		if(g[u]!=-1)return;
    		for(int i=beg[u];i;i=las[i]){
    			int v=to[i];
    			get_g(v);
    			g[u]=max(g[u],g[v]+1);
    		}
    		if(g[u]==-1)g[u]=0;
    	}
    }
    
    void init(){
    	read(n); read(m);
    	int u,v;
    	REP(i,1,m)read(u),read(v),add(u,v);
    	REP(i,1,n)add(0,i),add(i,n+1);
    
    	memset(f,-1,sizeof(f));
    	memset(g,-1,sizeof(g));
    	dp::get_f();
    	dp::get_g(0);
    }
    
    int val[maxm],deg[maxn],ans=inf,id;
    queue<int>qu;
    vector<int>pre[maxn];
    priority_queue<pii>h;
    bool del[maxm];
    
    void work(){
    	REP(i,2,cnte){
    		val[i]=f[from[i]]+g[to[i]]+1;
    		++deg[to[i]];
    	}
    	qu.push(0);
    	while(!qu.empty()){
    		int u=qu.front(); qu.pop();
    		REP(i,0,pre[u].size()-1)
    			del[pre[u][i]]=1;
    		while(!h.empty() && del[h.top().se])h.pop();
    		if(!h.empty() && h.top().fi<ans)ans=h.top().fi,id=u;
    		for(int i=beg[u];i;i=las[i]){
    			int v=to[i]; --deg[v];
    			h.push(mk(val[i],i));
    			pre[v].pb(i);
    			if(!deg[v])qu.push(v);
    		}
    	}
    	printf("%d %d
    ",id,ans-2);
    }
    
    int main(){
    	File();
    	init();
    	work();
    	return 0;
    }
    
    
  • 相关阅读:
    [学习]利用SqlDataAdapter Insertcommand 获取刚新增的自动编号ID值
    [转]下拉框OnChange触发文本框值变化
    .NET伪静态使用以及和纯静态的区别
    Java之替换“\n”符号 Binary
    【转载】三种东西永远不要放到数据库里 Binary
    如何为Android Spinner设置一个初始值(How to make an Android Spinner with initial text “Select One”)? Binary
    Android之SQLite列操作 Binary
    Android——调用系统相册 Binary
    Android——用XML的selector实现按钮多态 Binary
    keystore信息的查看 Binary
  • 原文地址:https://www.cnblogs.com/ylsoi/p/9861154.html
Copyright © 2011-2022 走看看