Solution
题目中的 “(+)” 和 “(-)” 都蕴含着大小关系,所以考虑差分约束。
对于每两个砝码之间有四种情况,我们可以分别列出他们的不等关系
- 如果是 (i = j), 那么有 (0 le i - j le 0)
- 如果是 (i > j),那么有 (1 le i - j le 2)
- 如果是 (i < j), 那么有 (-2 le i - j le -1)
- 如果是 (?),那么有 (-2 le i - j le 2)
不等式的范围取决于两个砝码 (i,j) 极限时能取的的情况
我们要知道在所有这些约束条件都满足的情况下,两个砝码之差的范围
那么需要一个最大值和最小值。
我们知道差分约束中最长路可以求最小值,最短路可以求最大值,那么根据上面的不等关系拆开建边即可。
这里采用 ( ext{Floyd}) 求最短路,这样两点之间的差也好表示:
设 (Dis_{i,j}) 是 (j - i) 可取的最小值, (dis_{i,j}) 是 (j - i) 可取的最大值,即
[Dis_{i,j} le j-i le dis_{i,j}
]
为什么要求两个砝码之差?
当统计左边重的情况时,有 (A+B>i+j),稍微转换一下变成 (A - i > j - B),此时出现了差的形式。对于三个统计的情况都可以这样变换。
- 当统计左边重时,有 (A+B>i+j),转化成 (A-i>j-B)。那么只有 (A-i) 的最小值大于 (j-B) 的最大值或者 (B-i) 的最小值大于 (j - A) 的最大值才有贡献。
- 当统计右边重时,和左边的情况相反。类比推理即可。
- 当统计两边一样重时,(A+B = i+j),转换成 (A-i = B-j)。那么只有 (A-i) 的最小值等于 (B-j) 的最大值并且 (A-i) 的最大值等于 (B-j) 的最小值 或者 (A,B)交换后成立的时候才有贡献。
和 (A,B) 匹配的 (i,j) 暴力枚举即可。
总的复杂度是 (O(n^3))。
更多细节看代码。
Code
/*
Work by: Suzt_ilymics
Problem: 不知名屑题
Knowledge: 垃圾算法
Time: O(能过)
*/
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#define LL long long
#define orz cout<<"lkp AK IOI!"<<endl
using namespace std;
const int MAXN = 1e5+5;
const int INF = 1e9+7;
const int mod = 1e9+7;
int n, A, B;
char s[55][55];
int dis[55][55], Dis[55][55];
int read(){
int s = 0, f = 0;
char ch = getchar();
while(!isdigit(ch)) f |= (ch == '-'), ch = getchar();
while(isdigit(ch)) s = (s << 1) + (s << 3) + ch - '0' , ch = getchar();
return f ? -s : s;
}
int main()
{
n = read(), A = read(), B = read();
for(int i = 1; i <= n; ++i) cin >> s[i] + 1;
for(int i = 1; i <= n; ++i) {
for(int j = 1; j <= n; ++j) {
if(s[i][j] == '=') {
dis[i][j] = 0, Dis[i][j] = 0; // 0 <= i - j <= 0
} else if(s[i][j] == '+') {
dis[i][j] = -1, Dis[i][j] = -2; // 1 <= i - j <= 2
} else if(s[i][j] == '-') {
dis[i][j] = 2, Dis[i][j] = 1; // -2 <= i - j <= -1
} else {
dis[i][j] = 2, Dis[i][j] = -2; // -2 <= i - j <= 2
}
}
}
for(int k = 1; k <= n; ++k) {
for(int i = 1; i <= n; ++i) {
if(i == k) continue;
for(int j = 1; j <= n; ++j) {
if(k == j || i == j) continue;
dis[i][j] = min(dis[i][j], dis[i][k] + dis[k][j]); // 最短路跑最大值
Dis[i][j] = max(Dis[i][j], Dis[i][k] + Dis[k][j]); // 最长路跑最小值
}
}
}
int cnt1 = 0, cnt2 = 0, cnt3 = 0;
for(int i = 1; i <= n; ++i) {
if(i == A || i == B) continue;
for(int j = 1; j < i; ++j) { // 暴力枚举 让每组砝码都选一遍
if(j == A || j == B) continue;
if(Dis[i][A] > dis[B][j] || Dis[i][B] > dis[A][j] ) // A + B > i + j -> A - i > j - B
cnt1++;
if((dis[i][A] == Dis[B][j] && Dis[i][A] == dis[B][j]) || (dis[i][B] == Dis[A][j] && Dis[i][B] == dis[A][j])) // A + B == i + j
cnt2++;
if(Dis[A][i] > dis[j][B] || Dis[B][i] > dis[j][A] ) // A + B < i + j -> i - A > B - j
cnt3++;
}
}
printf("%d %d %d", cnt1, cnt2, cnt3);
return 0;
}