本题不好下手 , 所以提示一下 , 大家就可以不用看我代码下的分析了 , 这样对训练思维大有裨益。
提示:
1. 相互等于的量其实没有必要分开讨论 , 因为她们任何时候都是一个整体
2. 如果两个量本身就不联通 , 那么的约束关系就很弱。 比如
a<b
,
c<d
那么
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. 枚举天平右端的两个数 , 并且枚举这四个数的值 , 看是否合法。
注意: 每个数的可能取值要考虑到他是否有前驱和后继 , 代码中步骤写的很详细