【NOIP2017提高A组模拟9.23】碎 题解
2 − S A T 2-SAT 2−SAT详解请看这里。
题目描述
解题方法
40分思路
设第一瓣陌生度为
f
A
f_A
fA,第二瓣为
f
B
f_B
fB。
枚举
f
A
f_A
fA,二分枚举
f
B
f_B
fB(假设
f
A
≥
f
B
f_A\geq f_B
fA≥fB),然后用
2
−
S
A
T
2-SAT
2−SAT判断一下。
2 − S A T 2-SAT 2−SAT判断
这里的就把每个点拆成是 A A A图还是 B B B图,用两个点。
- 大于
f
A
f_A
fA的边,我们不能让他们在同一个集合,所以连成这样:
- 在
f
B
f_B
fB与
f
A
f_A
fA范围内的边,我们只能放在
A
A
A集合或者不放,所以连成这样:
满分思路
然后考虑优化。
我们用并查集从大到小建图,然后判断环。
情况1
如果这是一个图的偶环,那么最短边不可能与答案有关,舍去。
情况2
如果这是一个图的奇环,那么最短边是最第一瓣陌生度的下界,也就是说,第一瓣的陌生度一定大于等于这条边,所以只要看大于等于它的边,因为是从大到小,所以直接 b r e a k break break。
这就是优化,可以做到
O
(
n
3
log
2
n
)
O(n^3\log_2^n)
O(n3log2n)。
听说好可以做到
O
(
n
3
)
O(n^3)
O(n3),不知道。
代码
#include<bits/stdc++.h>
#define N 160005
using namespace std;
struct Node{
int x,y,v;
}a[N];
int A[N],M,f[N],g[N];
int n,m,ans,cnt,la[N],low[N],dfn[N],sta[N],sum,top,col[N],tot,vis[N],to[N],ne[N];
bool cmp(Node a1,Node a2){
return a1.v>a2.v;
}
void add(int x,int y){
to[++cnt]=y;
ne[cnt]=la[x];
la[x]=cnt;
}
void add1(int x,int y){
add(x+n,y);
add(x,y+n);
add(y+n,x);
add(y,x+n);
}
void add2(int x,int y){
add(x+n,y);
add(y+n,x);
}
void tarjan(int x){
dfn[x]=low[x]=++tot;
vis[x]=1;
sta[++top]=x;
for(int i=la[x];i;i=ne[i]){
int y=to[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]){
int y=0;
sum++;
while(y^x){
y=sta[top--];
vis[y]=0;
col[y]=sum;
}
}
}
int check(int da,int db){
memset(la,0,sizeof(la));
cnt=0;
for(int i=1;i<=m;i++)
if(a[i].v>da) add1(a[i].x,a[i].y);
else if(a[i].v>db) add2(a[i].x,a[i].y);
memset(vis,0,sizeof(vis));
memset(dfn,0,sizeof(dfn));
memset(col,0,sizeof(col));
sum=top=tot=0;
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 0;
return 1;
}
int find(int x){
if(x==f[x]) return x;
else{
int y=find(f[x]);
g[x]^=g[f[x]];
return f[x]=y;
}
}
int main(){
freopen("broken.in","r",stdin);
freopen("broken.out","w",stdout);
scanf("%d",&n);
ans=2000000001;
for(int i=1;i<n;i++)
for(int j=i+1;j<=n;j++){
int x;
scanf("%d",&x);
a[++m].v=x;
a[m].x=i;
a[m].y=j;
}
sort(a+1,a+m+1,cmp);
M=0;
for(int i=1;i<=n;i++) f[i]=i;
for(int i=1;i<=m;i++){
int x=find(a[i].x);
int y=find(a[i].y);
if(x==y){
if(g[a[i].x]==g[a[i].y]){
A[++M]=a[i].v;
break;
}
}
else{
if(x>y) swap(x,y);
f[y]=x;
g[y]=g[a[i].x]^g[a[i].y]^1;
A[++M]=a[i].v;
}
}
for(int i=1;i<=M;i++){
if(A[i]>=ans) continue;
if(check(A[i],0)){
ans=min(ans,A[i]);
continue;
}
int l=1,r=A[i],sum=-1;
while(l<=r){
int mid=(l+r)>>1;
if(check(A[i],mid)) r=mid-1,sum=mid;
else l=mid+1;
if(sum+A[i]>=ans) break;
}
if(sum!=-1) ans=min(ans,sum+A[i]);
}
printf("%d",ans);
}