BZOJ4078: [Wf2014]Metal Processing Plant

2 篇文章 0 订阅

不妨设 D(S)<=D(T) D ( S ) <= D ( T ) ,我们可以从大到小去枚举是哪条边贡献到 D(T) D ( T ) ,对于 D(S) D ( S ) 显然满足二分性,我们可以二分他,然后对于边权 >D(T) > D ( T ) 的边,他的两端点不能在同一个集合里,对于边权在 (D(S),D(T)] ( D ( S ) , D ( T ) ] 内的边,他的两端点不能都在A里,我们可以建出一个图跑 2SAT 2 − S A T ,那么当前的复杂度是 O(n4logn2) O ( n 4 l o g n 2 )

这个复杂度太大,我们尝试去优化 D(T) D ( T ) 的枚举,每次我们计算完 D(T) D ( T ) 的贡献后,将这条边建进图中,那么当图中存在奇环时就不需要再枚举了,因为这些边权 >D(T) > D ( T ) ,两端点不能在同一个集合里,奇环说明这个图不是二分图,就不能做到任意一条边的两端都不在一个集合里
此外还有一个性质,就是这个图中任意一个偶环的最小边一定不会成为 D(T) D ( T ) ,因为如果他对 D(T) D ( T ) 产生贡献,说明他的两端点在同一集合里,因为他是个偶环,所以一定还存在另外一条边,满足两端点在同一集合里且权值>当前枚举的D(T)

所以我们枚举边时,将他加入图中,若他在一个偶环上,则不需要处理这条边,若他在一个奇环上,计算完这条边的贡献后就可以退出,那么我们实际上只需要对 O(n) O ( n ) 条边去二分统计贡献,复杂度就降到了 O(n3logn2) O ( n 3 l o g n 2 )

code:

#include<set>
#include<map>
#include<deque>
#include<queue>
#include<stack>
#include<cmath>
#include<ctime>
#include<bitset>
#include<string>
#include<vector>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<climits>
#include<complex>
#include<iostream>
#include<algorithm>
#define ll long long
using namespace std;

inline void read(int &x)
{
    char c;while(!((c=getchar())>='0'&&c<='9'));
    x=c-'0';
    while((c=getchar())>='0'&&c<='9') (x*=10)+=c-'0';
}
inline void down(int &a,const int &b){if(a>b)a=b;}
const int maxn = 505;

int n;
int mp[maxn][maxn];
struct edge{int y,nex;}a[maxn*maxn*2]; int len,fir[maxn];
inline void ins(const int x,const int y){a[++len]=(edge){y,fir[x]};fir[x]=len;}

int dfn[maxn],low[maxn],id;
int t[maxn],tp; bool insta[maxn];
int bel[maxn],cnt;
void tarjan(const int x)
{
    dfn[x]=low[x]=++id;
    insta[t[++tp]=x]=true;
    for(int k=fir[x],y=a[k].y;k;k=a[k].nex,y=a[k].y)
    {
        if(!dfn[y]) tarjan(y),down(low[x],low[y]);
        else if(insta[y]) down(low[x],dfn[y]);
    }
    if(low[x]==dfn[x])
    {
        ++cnt; int la=0;
        while(la!=x)
        {
            insta[la=t[tp--]]=false;
            bel[la]=cnt;
        }
    }
}

int fa[maxn],fas[maxn];
int findfa(const int x)
{
    if(fa[x]==x) return x;
    int k=findfa(fa[x]);
    fas[x]=fas[x]^fas[fa[x]];
    return fa[x]=k;
}

struct ei
{
    int i,j,w;
    friend inline bool operator <(const ei x,const ei y){return x.w<y.w;}
}e[maxn*maxn]; int en;
bool judge(int x,int y)
{
    len=id=cnt=0; 
    memset(fir,0,sizeof fir);
    memset(dfn,0,sizeof dfn);
    for(int k=x+1;k<=en;k++)
    {
        int i=e[k].i,j=e[k].j;
        if(e[k].w>e[x].w) ins(i,j+n),ins(j,i+n);
        if(e[k].w>e[y].w) ins(i+n,j),ins(j+n,i);
    }
    for(int i=1;i<=2*n;i++) if(!dfn[i]) tarjan(i);
    for(int i=1;i<=n;i++) if(bel[i]==bel[i+n]) return false;
    return true;
}
int ans;
void solve()
{
    for(int i=1;i<=n;i++) fa[i]=i,fas[i]=0;
    sort(e+1,e+en+1); e[en+1].w=e[en].w+1;
    for(int R=en;R>=1;R--)
    {
        int x=e[R].i,y=e[R].j;
        int f1=findfa(x),f2=findfa(y);
        if(f1==f2&&fas[x]!=fas[y]) continue;

        //search
        int l=0,r=R-1;
        while(l<=r)
        {
            int mid=l+r>>1;
            if(judge(mid,R)) r=mid-1;
            else l=mid+1;
        }
        down(ans,e[R].w+e[++r].w);

        //merge
        if(f1!=f2) fa[f1]=y,fas[f1]=!fas[x];
        else return;
    }
}

int main()
{
    //freopen("tmp.in","r",stdin);
    //freopen("tmp.out","w",stdout);

    while(scanf("%d",&n)!=EOF)
    {
        en=0;
        for(int i=1;i<n;i++) for(int j=i+1;j<=n;j++)
        {
            read(mp[i][j]);
            mp[j][i]=mp[i][j];
            e[++en]=(ei){i,j,mp[i][j]};
        }
        if(n<=2) { puts("0");continue; }
        ans=INT_MAX; solve();
        printf("%d\n",ans);
    }

    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值