题解:P6898 [ICPC 2014 WF] Metal Processing Plant

前言

来自山西队爷的分享题。

思路分析

首先有一个朴素的想法,能否枚举两个集合的边权,判断是否合法。

发现是可以的。

具体地,我们设 \(D(A)=w_1,D(B)=w_2\),不失一般性,假设 \(w_1 \ge w_2\)

这样,我们可以把图上的边 \((x,y,w)\) 分成三类:

  • \(w \le w_2\):没有影响;

  • \(w2<w\le w_1\)\(x,y\) 不能同时在 \(B\) 集合中;

  • \(w>w_1\)\(x,y\) 不能同时在一个集合中。

发现限制很像一个 2-sat 问题,考虑将 \(x\) 拆成 \(x',x\),分别表示在 \(A,B\) 集合中。

  • 对于不能同时在 \(B\) 集合的点,\(x\to y'\)\(y \to x'\)

  • 对于不能同时在一个集合中的点,\(x\to y'\)\(y\to x'\)\(x' \to y\)\(y' \to x\)

然后直接 tarjan 判定是否合法。

复杂度 \(O(n^6)\)

能不能再给力一点啊?

大胆猜测单调性,也就是 \(w_1\) 递增时,合法的 \(w_2\) 递减,这样可以双指针维护。

复杂度 \(O(n^4)\)

能不能再给力一点啊?

考虑对于不能在同一个集合的限制,实际上也太严苛了。

考虑从大到小枚举 \(w_1\),二分求出 \(w_2\) 的过程中,我们在线维护 \(w \ge w_1\) 的边的连通性,如果当前边连上出现环,分类讨论:

  • 如果是奇环,那么以后所有的边都加不进去了,考虑做完这次直接终止;

  • 如果是偶环,那么图的联通性并没有改变,二分出的 \(w_2\) 结果不会改变,直接更新答案就行。

判断环的奇偶性可以带权并查集。

分析我们的复杂度,我们本质不同的 \(w_1\) 只有 \(O(n)\) 个。

复杂度 \(O(n^3 \log n)\),常数不大,可以通过。

代码实现

要特别注意 \(0\) 的问题。

#include<bits/stdc++.h>
using namespace std;
const int inf=2e9+10;
int n,m,cnt,p,ans,v;
struct node{
	int x,y,w;
}h[40005];
bool cmp(node a,node b){
	return a.w<b.w;
}
int fa[205],w[205];
void init(){
	for(int i=1;i<=n;i++){
		fa[i]=i;
	}
}
int find(int x){
	if(fa[x]==x) return x;
	int y=find(fa[x]);
	w[x]^=w[fa[x]];
	fa[x]=y;
	return y; 
}
void merge(int x,int y){
	int fx=find(x),fy=find(y);
	if(fx==fy) return;
	fa[fy]=fx;
	w[fy]=w[x]^w[y]^1;
}
int head[405],nxt[400005],target[400005],tot;
void add(int x,int y){
	tot++;
	nxt[tot]=head[x];
	head[x]=tot;
	target[tot]=y;
} 
int dfn[405],low[405],vis[405],col[405],dcnt,ccnt;
stack<int> s;
void tarjan(int x){
	dfn[x]=low[x]=++dcnt;
	vis[x]=1;
	s.push(x);
	for(int i=head[x];i;i=nxt[i]){
		int y=target[i];
		if(!dfn[y]){
			tarjan(y);
			low[x]=min(low[x],low[y]);
		}else if(vis[y]) low[x]=min(low[x],dfn[y]);
	}
	if(dfn[x]==low[x]){
		ccnt++;
		col[x]=ccnt;
		vis[x]=0;
		while(s.top()!=x){
			int y=s.top();
			s.pop();
			col[y]=ccnt;
			vis[y]=0;
		}
		s.pop();
	}
}
bool check(int w1,int w2){
	for(int i=1;i<=(n<<1);i++){
		head[i]=dfn[i]=low[i]=vis[i]=col[i]=0;
	}
	dcnt=ccnt=tot=0;
	for(int i=1;i<=cnt;i++){
		if(h[i].w<=w2){
			continue;
		}else if(h[i].w>w1){
			add(h[i].x,h[i].y+n);
			add(h[i].y+n,h[i].x);
			add(h[i].y,h[i].x+n);
			add(h[i].x+n,h[i].y);
		}else{
			add(h[i].x,h[i].y+n);
			add(h[i].y,h[i].x+n);
		}
	}
	for(int i=1;i<=(n<<1);i++){
		if(!dfn[i]) tarjan(i);
	}
	for(int i=1;i<=n;i++){
		if(col[i]==col[i+n]) return false;
	}
	return true;
}
int find(int l,int r,int w){
	if(l==r){
		if(check(w,h[l].w)) return l;
		else return -1;
	}
	int mid=(l+r)>>1;
	if(check(w,h[mid].w)) return find(l,mid,w);
	else return find(mid+1,r,w); 
}
int main(){
	ios::sync_with_stdio(false);
	cin.tie(0);
	cout.tie(0);
	cin>>n; 
	for(int i=1;i<=n;i++){
		for(int j=i+1;j<=n;j++){
			cin>>v;
			h[++cnt]=(node){i,j,v};
		}
	}
	sort(h+1,h+1+cnt,cmp);
	if(check(0,0)){
		cout<<0;
		return 0;
	}
	p=0,ans=inf;
	init();
	for(int i=cnt;i>=1;i--){
		if(find(h[i].x)==find(h[i].y)){
			if(w[h[i].x]==w[h[i].y]){
				p=find(p,cnt,h[i].w);
			    if(p!=-1) ans=min(ans,h[i].w+h[p].w);
			    break;
			}else ans=min(ans,h[i].w+h[p].w);
		}else{
			merge(h[i].x,h[i].y);
			p=find(p,cnt,h[i].w);
			if(p==-1) break;
			ans=min(ans,h[i].w+h[p].w);
		}
	}
	cout<<ans;
	return 0;
}
posted @ 2025-03-24 19:20  _Kenma  阅读(23)  评论(0)    收藏  举报