BZOJ4078: [Wf2014]Metal Processing Plant(2-SAT)

传送门

题解:
我们枚举最大边,然后再二分第二大边即可。
注意到加入最大边后如果和不能选的比他大的边形成了偶环,那么一定有一个之前的出现过,我们可以跳过对他的判断。 如果是奇环就一定会出现一条,这时后面的都不用判断了。
时间复杂度 O(n3logn) O ( n 3 log ⁡ n )

#include <bits/stdc++.h>
using namespace std;
typedef pair <int,int> pii;

const int RLEN=1<<18|1;
inline char nc() {
    static char ibuf[RLEN],*ib,*ob;
    (ib==ob) && (ob=(ib=ibuf)+fread(ibuf,1,RLEN,stdin));
    return (ib==ob) ? -1 : *ib++;
}
inline int rd() {
    char ch=nc(); int i=0,f=1;
    while(!isdigit(ch)) {if(ch=='-')f=-1; ch=nc();}
    while(isdigit(ch)) {i=(i<<1)+(i<<3)+ch-'0'; ch=nc();}
    return i*f;
}

const int N=4e2+50;
int n,m,anc[N],val[N],ans=0x3f3f3f3f;
struct E {
    int x,y,w;
    E() {}
    E(int x,int y,int w) : x(x), y(y), w(w) {}
    friend inline bool operator <(const E &a,const E &b) {return a.w<b.w;}
} e[N*N];
inline pii ga(int x) {
    if(x==anc[x]) return pii(x,0);
    pii f=ga(anc[x]);
    val[x]=val[x]^f.second;
    anc[x]=f.first;
    return pii(anc[x],val[x]);
}
map <int,int> S;
vector <int> edge[N];
int dfn[N],bl[N],st[N],low[N],ins[N],top,ind,scc;
inline void dfs(int x) {
    dfn[x]=low[x]=++ind; ins[st[++top]=x]=1;
    for(int e=edge[x].size()-1;e>=0;e--) {
        int v=edge[x][e]; 
        if(!dfn[v]) dfs(v), low[x]=min(low[x],low[v]);
        else if(ins[v]) low[x]=min(low[x],dfn[v]);
    }
    if(low[x]==dfn[x]) {
        ++scc;
        int u; do {
            u=st[top--];
            ins[u]=0;
            bl[u]=scc;
        } while(u!=x);
    }
}
inline bool check(int lim1,int lim2) {
    for(int i=1;i<=2*n;i++) edge[i].clear();
    memset(dfn,0,sizeof(dfn)); ind=scc=0;

    for(int i=1;i<=m;i++) {
        if(e[i].w>lim1) {
            edge[e[i].x*2-1].push_back(e[i].y*2);
            edge[e[i].y*2-1].push_back(e[i].x*2);
        }
        if(e[i].w>lim2) {
            edge[e[i].x*2].push_back(e[i].y*2-1);
            edge[e[i].y*2].push_back(e[i].x*2-1); 
        }
    }

    for(int i=1;i<=2*n;i++) if(!dfn[i]) dfs(i);
    for(int i=1;i<=n;i++) if(bl[i*2]==bl[i*2-1]) return false;
    return true;
}
inline void calc(int A) {
    if(S[A]) return; S[A]=1;
    int l=0,r=A;
    while(l<=r) {
        int mid=(l+r)>>1;
        if(check(A,mid)) {ans=min(ans,mid+A); r=mid-1;}
        else l=mid+1;
    }
}
int main() {
    n=rd();
    for(int i=1;i<=n;i++) anc[i]=i;
    for(int i=1;i<=n;i++)
        for(int j=i+1;j<=n;j++)
            e[++m]=E(i,j,rd());
    sort(e+1,e+m+1);
    for(int i=m;i>=1;i--) {
        int x=e[i].x, y=e[i].y;
        pii xx=ga(x), yy=ga(y);
        if(xx.first==yy.first) {
            if(xx.second!=yy.second) continue;
            else {calc(e[i].w); break;}
        } calc(e[i].w);
        anc[xx.first]=yy.first;
        val[xx.first]=xx.second^yy.second^1;
    } calc(0); cout<<ans<<'\n';
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值