[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);
}