zoukankan      html  css  js  c++  java
  • IOI2021集训队作业 230AG Metal Processing Plant

    读入一个完全图,你要把所有的点分成两个集合(A,B),使得(max_{u,vin A} d_{u,v}+max_{u,vin B} d_{u,v})最小。

    (d)表示边。

    (nle 200)


    钦定(A)的贡献小于等于(B)。有个暴力的做法:枚举(B)的贡献,然后二分(A)的贡献,得到一系列限制:要求两个点不能再同个集合,或要求两个点不能同时在(A)集。这个东西可以two-sat解决。然后时间复杂度是(O(n^4lg n))

    考虑优化:考虑大于(B)的贡献的边形成的图,这必须要是个二分图;如果某个时候出现了奇环,就可以不用计算了;当有新边加入这个图时,如果边连着的两个点之前已经确定了不在同个集合中(也就是不在同个二分图连通块中),那也不需要计算。要计算的时候只有两个连通块合并的时候,那么只需要计算(O(n))次。于是总时间复杂度为(O(n^3lg n))

    应该还可以优化成(O(n^3))。不过我没写。


    using namespace std;
    #include <cstdio>
    #include <cstring>
    #include <algorithm>
    #define N 205
    #define INF 1000000000
    int n,m;
    struct edge{int u,v,w;} ed[N*N];
    bool cmpe(edge a,edge b){return a.w<b.w;}
    struct EDGE{
    	int to;
    	EDGE *las;
    };
    struct Graph{
    	EDGE e[N*N*2];
    	int ne;
    	EDGE *last[N*2];
    	void link(int u,int v){
    		e[ne]={v,last[u]};
    		last[u]=e+ne++;
    	}
    	void clear(){
    		ne=0;
    		memset(last,0,sizeof(EDGE*)*(2*n+1));
    	}
    } G;
    int dsu[N],xo[N];
    void getdsu(int x){
    	if (dsu[x]==x) return;
    	getdsu(dsu[x]);
    	xo[x]^=xo[dsu[x]];
    	dsu[x]=dsu[dsu[x]];
    }
    int dfn[N*2],low[N*2],nowdfn;
    int st[N*2],tp;
    bool ins[N*2];
    int col[N*2],nc;
    void tarjan(int x){
    	dfn[x]=low[x]=++nowdfn;
    	st[++tp]=x;
    	ins[x]=1;
    	for (EDGE *ei=G.last[x];ei;ei=ei->las)
    		if (!dfn[ei->to])
    			tarjan(ei->to),low[x]=min(low[x],low[ei->to]);
    		else if (ins[ei->to])
    			low[x]=min(low[x],dfn[ei->to]); 
    	if (dfn[x]==low[x]){
    		++nc;
    		while (st[tp]!=x){
    			ins[st[tp]]=0;
    			col[st[tp--]]=nc;
    		}
    		ins[x]=0;
    		col[x]=nc;
    		tp--;
    	}
    }
    
    bool judge(int da,int db){
    	G.clear();
    	for (int i=m;i>db;--i){
    		int u=ed[i].u,v=ed[i].v;
    		G.link(u+n,v),G.link(u,v+n);
    		G.link(v+n,u),G.link(v,u+n);
    	}
    	for (int i=db;i>da;--i){
    		int u=ed[i].u,v=ed[i].v;
    		G.link(u+n,v);
    		G.link(v+n,u);
    	}
    	memset(dfn,0,sizeof(int)*(n*2+1));
    	nc=0;
    	for (int i=1;i<=n*2;++i)
    		if (!dfn[i])
    			tarjan(i);
    	for (int i=1;i<=n;++i)
    		if (col[i]==col[i+n])
    			return 0;
    	return 1;
    }
    int ans;
    void work(int lim){
    	int l=0,r=lim,res=INF;
    	while (l<=r){
    		int mid=l+r>>1;
    		if (judge(mid,lim)){
    			res=ed[mid].w;
    			r=mid-1;
    		}
    		else
    			l=mid+1;
    	}
    	ans=min(ans,res+ed[lim].w);
    }
    int main(){
    //	freopen("in.txt","r",stdin);
    //	freopen("out.txt","w",stdout);
    	scanf("%d",&n);
    	if (n==1){
    		printf("0
    ");
    		return 0;
    	}
    	for (int i=1;i<=n;++i){
    		for (int j=i+1;j<=n;++j){
    			int w;
    			scanf("%d",&w);
    			ed[++m]={i,j,w};
    		}
    	}
    	for (int i=1;i<=n;++i)
    		dsu[i]=i,xo[i]=0;
    	sort(ed+1,ed+m+1,cmpe);
    	ans=ed[m].w;
    	int i;
    	for (i=m;i>=1;--i){
    		int u=ed[i].u,v=ed[i].v;
    		getdsu(u),getdsu(v);
    		if (dsu[u]!=dsu[v]){
    			work(i);
    			xo[dsu[u]]=xo[u]^1^xo[v];
    			dsu[dsu[u]]=dsu[v];
    		}
    		else{
    			if (xo[u]==xo[v]){
    				work(i);
    				break;
    			}
    		}
    	}
    	if (i==0)
    		work(0);
    	printf("%d
    ",ans);
    	return 0;
    }
    
  • 相关阅读:
    Java并发专题 带返回结果的批量任务执行
    angualejs
    Java并发编程:Callable、Future和FutureTask
    mybatis
    InitialContext和lookup
    git 常用使用命令
    junit spring 测试
    redis windows
    为何PS出的RSS总和大于实际物理内存
    32位机器的LowMemory
  • 原文地址:https://www.cnblogs.com/jz-597/p/13960858.html
Copyright © 2011-2022 走看看