[ICPC2014 WF] Metal Processing Plant

一、题目

点此看题

二、解法

考虑枚举两部分的权值分别是 \(A,B\),不妨设 \(A>B\),那么会带来这样的限制:

  • 如果 \(w(i,j)>A\),说明这两个点不能同时处在一个集合中。
  • 如果 \(A\geq w(i,j)>B\),说明这两个点不能同时处在第二个集合中。
  • 如果 \(B\geq w(i,j)\),没有限制。

可以用 \(\tt 2sat\) 解决这种不能共存类限制,建边方式是平凡的。暴力枚举之后跑 \(\tt 2sat\) 时间复杂度 \(O(n^6)\),可以枚举 \(A\) 然后双指针做到 \(O(n^4)\)

因为每两次的图十分相近,所以这样做也很浪费时间。考虑从大到小枚举 \(A\),这样有一条边会变成第一类限制,我们考虑只保留第一类限制的图有什么效果:

  • 如果加边后出现偶环,考虑如果用到它作为最大值两端应该同色,但是这两端又隔着奇数个点,一定是异色的,所以不会对答案产生影响,可以直接跳过。
  • 如果加边后出现奇环,说明最大值不可能再更小了,做完这一次就可以直接结束。
  • 否则暴力做,直接二分 \(B\)

那么一共只会进行 \(O(n)\) 次二分,时间复杂度 \(O(n^3\log n)\)

三、总结

这个做法来源于:只考虑第一类限制,问题会变成平凡的二分图染色,那么分析奇环偶环方便我们对情况进行一些取舍。

#include <cstdio>
#include <vector>
#include <iostream>
#include <algorithm>
#include <stack>
using namespace std;
const int M = 405;
int read()
{
	int x=0,f=1;char c;
	while((c=getchar())<'0' || c>'9') {if(c=='-') f=-1;}
	while(c>='0' && c<='9') {x=(x<<3)+(x<<1)+(c^48);c=getchar();}
	return x*f;
}
int n,m,ans,fa[M],vl[M];vector<int> g[M];
int Ind,cnt,dfn[M],low[M],col[M],in[M];stack<int> s;
struct node
{
	int u,v,c;
	bool operator < (const node &b) const
		{return c<b.c;}
}e[M*M];
int find(int x)
{
	if(x==fa[x]) return x;
	int t=find(fa[x]);
	vl[x]^=vl[fa[x]];
	fa[x]=t;return t;
}
void add(int u,int v)
{
	g[u].push_back(v);
	g[v^1].push_back(u^1);
}
void tarjan(int u)
{
	in[u]=1;s.push(u);
	dfn[u]=low[u]=++Ind;
	for(int v:g[u])
	{
		if(!dfn[v])
		{
			tarjan(v);
			low[u]=min(low[u],low[v]);
		}
		else if(in[v])
			low[u]=min(low[u],dfn[v]);
	}
	if(low[u]==dfn[u])
	{
		int v=0;cnt++;
		do
		{
			v=s.top();s.pop();
			col[v]=cnt;in[v]=0;
		}while(u!=v);
	}
}
int check(int A,int B)
{
	for(int i=2;i<=2*n+1;i++)
		g[i].clear(),dfn[i]=low[i]=0;
	cnt=Ind=0;
	for(int i=1;i<=m;i++)
	{
		int u=e[i].u,v=e[i].v;
		if(e[i].c>A) add(u<<1,v<<1|1);
		if(e[i].c>B) add(u<<1|1,v<<1);
	}
	for(int i=2;i<=2*n+1;i++)
		if(!dfn[i]) tarjan(i);
	for(int i=1;i<=n;i++)
		if(col[i<<1]==col[i<<1|1]) return 0;
	return 1;
}
void work(int x)
{
	int l=0,r=x;
	while(l<=r)
	{
		int mid=(l+r)>>1;
		if(check(x,mid))
			ans=min(ans,x+mid),r=mid-1;
		else l=mid+1;
	}
}
signed main()
{
	n=read();ans=2e9;
	for(int i=1;i<=n;i++) fa[i]=i;
	for(int i=1;i<=n;i++)
		for(int j=i+1;j<=n;j++)
			e[++m]=node{i,j,read()};
	sort(e+1,e+1+m);
	for(int i=m;i>=1;i--)
	{
		int u=e[i].u,v=e[i].v;
		if(find(u)==find(v))
		{
			if(vl[u]!=vl[v]) continue;
			else {work(e[i].c);break;}
		}
		work(e[i].c);
		int x=fa[u],y=fa[v];
		fa[x]=y;vl[x]=vl[u]^vl[v]^1;
	}
	work(0);printf("%d\n",ans);
}
posted @ 2022-06-08 22:32  C202044zxy  阅读(77)  评论(1编辑  收藏  举报