zoukankan      html  css  js  c++  java
  • 题解 CF343E-Pumping Stations

    (Large atural) CF343E Pumping Stations / 原题链接

    解法

    前置知识点:最小割树。如果没有学过,可以去看看模板题。

    首先求出最小割树。然后,我们设 (c_{i,j}) 代表点 (i) 到点 (j) 的最小割。那么问题就转化为了:

    你有一个完全图,点 (i) 到点 (j) 边权为 (c_{i,j}),且边权是正数。(c_{i,j}=c_{j,i}) (所以是无向完全图)。现在你要找到一条路径(起点、终点不限),使得所有点都刚好经过一次。要找出总边权最大的这条路径。

    这个事实上是可以用贪心去做的。当到达一个点时,在所有「终点的点没有走过」的边中,找到权值最大的那一条,然后走。

    然后枚举一下起点,每个都按照上述贪心走一遍,找到总边权最大的那条路径就好了。

    贪心的正确性应该由完全图的性质可得。

    代码

    解法中的完全图不用真的建出来,循环一下就可以了。

    码风毒瘤见谅。

    #include<bits/stdc++.h>
    #define rep(i,x,y) for(int i=x;i<=y;++i)
    #define per(i,x,y) for(int i=x;i>=y;--i)
    #define mar(o,fst,e) for(int E=fst[o];E;E=e[E].nxt)
    #define v e[E].to
    #define vz ez[E].to
    using namespace std;
    const int n7=206,m7=2012,inf=INT_MAX-100;
    struct dino{int to,nxt,w,w0;}e[m7],ez[m7];
    int n,m,T,fst[n7],fstd[n7],ecnt0,ecnt=1,dep[n7],que[n7],ds,dt,ans;
    int fstz[n7],dept[n7],fc[n7][13],mni[n7][13],dot[n7],dotz[n7];
    int mc[n7][n7],ansx,ansn;bool u[n7];
    
    int rd(){
    	int shu=0;char ch=getchar();
    	while(!isdigit(ch))ch=getchar();
    	while(isdigit(ch))shu=(shu<<1)+(shu<<3)+ch-'0',ch=getchar();
    	return shu;
    }
    
    void Dedge(int sta,int edn,int w,dino *eh,int *fsth){
    	ecnt++;
    	eh[ecnt]=(dino){edn,fsth[sta],w,w};
    	fsth[sta]=ecnt;
    }
    
    void edge(int sta,int edn,int w,dino *eh,int *fsth){
    	Dedge(sta,edn,w,eh,fsth),Dedge(edn,sta,w,eh,fsth);
    }
    
    bool bfs(){
    	memset(dep,0,sizeof dep);
    	int head=1,tail=1;que[1]=ds;
    	dep[ds]=1,fstd[ds]=fst[ds];
    	while(head<=tail){
    		int o=que[head];
    		mar(o,fst,e){
    			if(dep[v]||e[E].w==0)continue;
    			dep[v]=dep[o]+1,fstd[v]=fst[v];
    			if(v==dt)return 1;
    			tail++,que[tail]=v;
    		}
    		head++;
    	}
    	return 0;
    }
    
    int dfs(int o,int val){
    	if(o==dt)return val;
    	int tot=val;
    	mar(o,fstd,e){
    		fstd[o]=E;
    		if(!tot){dep[o]=inf;break;}
    		if(dep[v]!=dep[o]+1||e[E].w==0)continue;
    		int out=dfs(v,min(tot,e[E].w));
    		e[E].w-=out,e[E^1].w+=out;
    		tot-=out;
    	}
    	return val-tot;
    }
    
    int dinic(int p,int q){
    	ds=p,dt=q;
    	rep(E,2,ecnt0)e[E].w=e[E].w0;
    	int tot=0;
    	while(bfs())tot+=dfs(ds,inf);
    	return tot;
    }
    
    void plant(int l,int r){
    	if(l==r)return;
    	edge(dot[l],dot[r],dinic(dot[l],dot[r]),ez,fstz);
    	int zuo=l-1,you=r+1;
    	rep(i,l,r){
    		if(dep[ dot[i] ])zuo++,dotz[zuo]=dot[i];
    		else you--,dotz[you]=dot[i];
    	}
    	rep(i,l,r)dot[i]=dotz[i];
    	plant(l,zuo),plant(you,r);
    }
    
    void dfst(int o){
    	rep(i,1,11)fc[o][i]=fc[ fc[o][i-1] ][i-1];
    	rep(i,1,11)mni[o][i]=min(mni[o][i-1],mni[ fc[o][i-1] ][i-1]);
    	mar(o,fstz,ez){
    		if(dept[vz])continue;
    		dept[vz]=dept[o]+1;
    		fc[vz][0]=o,mni[vz][0]=ez[E].w;
    		dfst(vz);
    	}
    }
    
    int Dlca(int p,int q){
    	if(dept[p]<dept[q])p^=q^=p^=q;
    	int fin=inf;
    	per(i,11,0){
    		if(dept[ fc[p][i] ]<dept[q])continue;
    		fin=min(fin,mni[p][i]);
    		p=fc[p][i];
    	}
    	if(p==q)return fin;
    	per(i,11,0){
    		if(fc[p][i]==fc[q][i])continue;
    		fin=min(fin,min(mni[p][i],mni[q][i]));
    		p=fc[p][i],q=fc[q][i];
    	}
    	fin=min(fin,min(mni[p][0],mni[q][0]));
    	return fin;
    }
    
    int solve(int o,bool sys){
    	memset(u,0,sizeof u);
    	int tot=0;u[o]=1;
    	if(sys)printf("%d ",o);
    	rep(i,1,n-1){//有n个点,起点已经确定,还有n-1个点要走
    		int maxx=0,maxn;
    		rep(j,1,n){
    			if(u[j])continue;//终点走过了的边就跳过
    			if(mc[o][j]>maxx)maxx=mc[o][j],maxn=j;//找到边权最大的
    		}
    		tot+=maxx,o=maxn,u[maxn]=1;//替换当前点
    		if(sys)printf("%d ",o);
    	}
    	return tot;
    }
    
    int main(){
    	n=rd(),m=rd();
    	rep(i,1,n)dot[i]=i;
    	rep(i,1,m){
    		int sta=rd(),edn=rd(),w=rd();
    		edge(sta,edn,w,e,fst);
    	}
    	ecnt0=ecnt,ecnt=0;
    	plant(1,n);
    	dept[1]=1,dfst(1);
        //前面都是最小割树的板子
    	rep(i,1,n)rep(j,i+1,n){
    		mc[i][j]=mc[j][i]=Dlca(i,j);
    	} 
        //mc[i][j]就是点i,j的最小割
    	rep(i,1,n){//枚举起点
    		int now=solve(i,0);
    		if(now>ansx)ansx=now,ansn=i;//如果更优,就选它
    	}
        //输出
    	printf("%d
    ",ansx);
    	solve(ansn,1);
    	return 0;
    }
    
  • 相关阅读:
    I NEED A OFFER!
    水题 Codeforces Round #303 (Div. 2) A. Toy Cars
    模拟 HDOJ 5099 Comparison of Android versions
    模拟 HDOJ 5095 Linearization of the kernel functions in SVM
    贪心 HDOJ 5090 Game with Pearls
    Kruskal HDOJ 1863 畅通工程
    Kruskal HDOJ 1233 还是畅通工程
    并查集 HDOJ 1232 畅通工程
    DFS/并查集 Codeforces Round #286 (Div. 2) B
    水题 Codeforces Round #286 (Div. 2) A Mr. Kitayuta's Gift
  • 原文地址:https://www.cnblogs.com/BlankAo/p/14227151.html
Copyright © 2011-2022 走看看