BZOJ1077 天平

本题不好下手 , 所以提示一下 , 大家就可以不用看我代码下的分析了 , 这样对训练思维大有裨益。

提示:
1. 相互等于的量其实没有必要分开讨论 , 因为她们任何时候都是一个整体
2. 如果两个量本身就不联通 , 那么的约束关系就很弱。 比如 a<b , c<d 那么 a c 的关系就没有约束。 但有一个例外 , a<b<d , e<c 那么此时 a,b,d 都是确定的 , 分别是 123 。 而 c>e , 所以 c>a
3. 现在我们要统计个数 , 因为数据很小 , 完全可以枚举另外两个是什么 , 然后在看看她们是否与A和B的关系是确定的就OK了。

详细说明见代码后:

#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <cmath>
#include <string>
#include <vector>
#include <deque>
#include <stack>
#include <algorithm>
#define FOR(va , a) for(int va=l[fa[a]];va<=r[fa[a]];va++)

using namespace std;
const int maxn = 55;

int n , A , B;
char s[maxn][maxn];

int fa[maxn];
int getfa(int a) { return fa[a]==a?a:(fa[a] = getfa(fa[a])); }
void link(int a , int b)
{
    if(getfa(a)==getfa(b)) return;
    fa[getfa(a)] = getfa(b);
}

int v[maxn];
int g[maxn][maxn];
int l[maxn] , r[maxn];

int cmp(int a) { return a==0?a:(a>0?1:-1); }

int main(int argc, char *argv[]) {

    scanf("%d%d%d" , &n , &A , &B); A--; B--;

    for(int i=0;i<n;i++) scanf("%s" , s[i]) , fa[i] = i;


    for(int i=0;i<n;i++) for(int j=0;j<n;j++) if(s[i][j]=='=') link(i, j);
    for(int i=0;i<n;i++) for(int j=0;j<n;j++) 
        if(s[i][j]=='+') g[getfa(i)][getfa(j)] = 1 , g[getfa(j)][getfa(i)] = -1;
        else if(s[i][j]=='-') g[getfa(i)][getfa(j)] = -1 , g[getfa(j)][getfa(i)] = 1;


    vector<int> rep;
    for(int i=0;i<n;i++) if(getfa(i)==i) rep.push_back(i);
    for(int i=0;i<rep.size();i++)
    {
        int b = 0 , s = 0;
        for(int j=0;j<rep.size();j++) b|= (g[rep[i]][rep[j]]==1) , s|= (g[rep[i]][rep[j]]==-1);
        if(!b || !s) continue;

        v[rep[i]] = 2;
        for(int j=0;j<rep.size();j++) 
            if(g[rep[i]][rep[j]]==1) v[rep[j]] = 1;
            else if(g[rep[i]][rep[j]]==-1) v[rep[j]] = 3;
    }


    for(int i=0;i<rep.size();i++)
    {
        int u = rep[i];
        l[u] = 1; r[u] = 3;

        if(v[u]) l[u] = r[u] = v[u];
        else
        {
            for(int j=0;j<rep.size();j++) 
                if(g[u][rep[j]]==1) l[u] = 2;
                else if(g[u][rep[j]]==-1) r[u] = 2;
        }
    }

    int r1 = 0 , r2 = 0 , r3 = 0;
    for(int i=0;i<n;i++) if(i!=A && i!=B) for(int j=i+1;j<n;j++) if(j!=A && j!=B)
    {
        int p1 = 0 , p2 = 0 ,p3 = 0;

        FOR(va, A) FOR(vb, B) FOR(vi, i) FOR(vj, j)
        {
            int relat[]={fa[A] , fa[B] , fa[i] , fa[j]};
            int relav[]={va , vb , vi , vj};
            bool ok = true;
            for(int t1=0;t1<4;t1++) for(int t2=t1+1;t2<4;t2++) 
                if(relat[t1]==relat[t2] && relav[t1]!=relav[t2]) { ok = false; t1 = 4; break; }
                else if(g[relat[t1]][relat[t2]] && g[relat[t1]][relat[t2]] != cmp(relav[t1]-relav[t2]))
                    { ok = false; t1 = 4; break; }
            if(!ok) continue;
            if(va+vb==vi+vj) p2 = 1;
            if(va+vb <vi+vj) p3 = 1;
            if(va+vb >vi+vj) p1 = 1;
        }
        if(p1+p2+p3==1) r1+=p1 , r2+=p2 , r3+=p3;
    }
    printf("%d %d %d\n" , r1 , r2 , r3);

    return 0;
}

详解:
1. 把相等的作为一个整体 , 每次都以并查集的祖先编号来访问。
2. 值不相等的进行连边 , 如果一个点既有前驱也有后继 , 那么这个点为2 , 更新所有与他相关的点的值。
3. 计算每个值相等的块的可能值 , 便于以后的枚举。
4. 枚举天平右端的两个数 , 并且枚举这四个数的值 , 看是否合法。

注意: 每个数的可能取值要考虑到他是否有前驱和后继 , 代码中步骤写的很详细

已标记关键词 清除标记
©️2020 CSDN 皮肤主题: 大白 设计师:CSDN官方博客 返回首页