• 题解 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;
    }
    
  • 相关阅读:
    一次访问ORACLE数据字典的优化
    利用分区优化SQL
    转行程序员的故事
    有一个苹果
    ubuntu10.04启动后出现grub rescue 模式
    source 命令 && . 命令
    ubuntu10.04启动后出现grub rescue 模式
    Android 各国语言缩写各国语言简称 .
    cpu的核心数、线程数、处理器的位数、操作系统的位数、能够支持最大内存 小结
    产品生产的各个阶段:DV,EV,PV ········是什么意思
  • 原文地址:https://www.cnblogs.com/BlankAo/p/14227151.html
Copyright © 2020-2023  润新知