BZOJ1077 天平

来源:互联网 发布:中国高铁在欧洲 知乎 编辑:程序博客网 时间:2024/04/28 13:02

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

提示:
1. 相互等于的量其实没有必要分开讨论 , 因为她们任何时候都是一个整体
2. 如果两个量本身就不联通 , 那么的约束关系就很弱。 比如 a<b , c<d 那么 ac 的关系就没有约束。 但有一个例外 , 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. 枚举天平右端的两个数 , 并且枚举这四个数的值 , 看是否合法。

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

0 0