bzoj1077 [SCOI2008]天平(并查集+暴力)

53 篇文章 0 订阅
43 篇文章 0 订阅

首先把有相等关系的都并成一个,方便讨论。
然后直接 O(n2) 暴力处理出每一个点的取值范围。
如果既有前驱又有后继,那这个点一定是2,然后暴力更新其他已知点。
未更新值的点可能有多种取值,根据连边关系限定。
然后暴力枚举C,D,以及A,B,C,D的取值,判断是否合法。以及是否答案为唯一。

#include <cstdio>
#include <cstring>
#include <algorithm>
#include <vector>
#include <queue>
using namespace std;
#define N 60
#define ll long long
#define inf 0x3f3f3f3f
inline int read(){
    int x=0,f=1;char ch=getchar();
    while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
    while(ch>='0'&&ch<='9') x=x*10+ch-'0',ch=getchar();
    return x*f;
}
int n,A,B,mp[N][N],l[N],r[N],fa[N],ans1,ans2,ans3;
char s[N][N];
inline int find(int x){return x==fa[x]?x:fa[x]=find(fa[x]);}
inline void merge(int x,int y){
    int xx=find(x),yy=find(y);
    if(xx!=yy) fa[xx]=yy;
}
int main(){
//  freopen("a.in","r",stdin);
    n=read();A=read();B=read();
    for(int i=1;i<=n;++i) fa[i]=i;
    for(int i=1;i<=n;++i){
        scanf("%s",s[i]+1);
        for(int j=1;j<=n;++j)
            if(s[i][j]=='=') merge(i,j);
    }for(int i=1;i<=n;++i){
        for(int j=1;j<=n;++j){
            if(s[i][j]=='+') mp[find(i)][find(j)]=1,mp[find(j)][find(i)]=-1;
            if(s[i][j]=='-') mp[find(i)][find(j)]=-1,mp[find(j)][find(i)]=1;
        }
    }for(int i=1;i<=n;++i){
        if(find(i)!=i) continue;
        int pre=0,succ=0;
        for(int j=1;j<=n;++j) pre|=(mp[i][j]==1),succ|=(mp[i][j]==-1);
        if(!pre||!succ) continue;
        l[i]=2;
        for(int j=1;j<=n;++j){
            if(mp[i][j]==1) l[j]=1;
            if(mp[i][j]==-1) l[j]=3;
        }
    }for(int i=1;i<=n;++i){
        if(fa[i]!=i) continue;
        if(l[i]){r[i]=l[i];continue;}
        l[i]=1;r[i]=3;
        for(int j=1;j<=n;++j){
            if(mp[i][j]==1) l[i]=2;
            if(mp[i][j]==-1) r[i]=2;
        }
    }int a=fa[A],b=fa[B];
    for(int C=1;C<=n;++C){
        if(C==A||C==B) continue;
        for(int D=C+1;D<=n;++D){
            if(D==A||D==B) continue;
            int c=fa[C],d=fa[D];
            int id[]={a,b,c,d},t1=0,t2=0,t3=0;
            for(int vc=l[c];vc<=r[c];++vc)
                for(int vd=l[d];vd<=r[d];++vd)
                    for(int va=l[a];va<=r[a];++va)
                        for(int vb=l[b];vb<=r[b];++vb){
                            bool flag=1;
                            int v[]={va,vb,vc,vd};
                            for(int i=0;i<4&&flag;++i)
                                for(int j=i+1;j<4&&flag;++j)
                                    if(id[i]==id[j]&&v[i]!=v[j]) flag=0;
                            for(int i=0;i<4&&flag;++i)
                                for(int j=i+1;j<4&&flag;++j)
                                    if(mp[id[i]][id[j]]==1&&v[i]<=v[j]||mp[id[i]][id[j]]==-1&&v[i]>=v[j]) flag=0;
                            if(!flag) continue;
                            if(va+vb>vc+vd) t1=1;
                            if(va+vb==vc+vd) t2=1;
                            if(va+vb<vc+vd) t3=1;
                        }
            if(t1+t2+t3==1) ans1+=t1,ans2+=t2,ans3+=t3;
        }
    }printf("%d %d %d\n",ans1,ans2,ans3);
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值