题解 P2474 【[SCOI2008]天平】
Preface
差分约束进阶题,有助于加深印象,也避免了对模板的固化。
刚开始写了 SPFA 后来发现不如 Floyd 方便就重构代码了……
Solution
题目有
原不等式等价于
传统的差分约束是直接求出一组解。但这里直接的搭配的解不唯一很不方便,我们尝试维护变量间的和差关系以直接满足不等关系。
此时,我们就可以设
即,跑两次 Floyd,一次跑最短路得到
用邻接矩阵建图的时候,
-
对于
+,i>j ,即i-j>0 ,可以得到i-j=1 或i-j=2 ,于是就有f(i,j)=2,g(i,j)=1 。 -
对于
-,有i-j=-1 或i-j=-2 ,即f(i,j)=-1,g(i,j)=-2 。 -
对于
=,有i=j ,即f(i,j)=g(i,j)=0 。 -
对于
?,i,j 关系未知,就假设两种最极端的情况,把f(i,j)=2,g(i,j)=-2 。
接下来就是如何判断不等关系成立。
对于
同理,对于
将上述做法推广到
-
对于
A+B=C+D ,等价于A-D=C-B 或A-C=D-B ,即f(A,D)=g(C,B)=g(A,D)=f(C,B) 或f(A,C)=g(D,B)=g(A,C)=f(D,B) 。 -
对于
A+B<C+D ,等价于A-C<D-B 或A-D<C-B ,即f(A,C)<g(D,B) 或f(A,D)<g(C,B) 。
时间复杂度
Notice
由于我们要对多个问进行统计答案,于是可以写个函数来多次调用,传参的时候给一个 lambda 表达式来当作比较函数即可。可以减少码量并方便调试。需要C++11。
其余的只是代码繁琐而已,细心就行。
Code
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
using namespace std;
typedef const int cint;
cint MAXN=55;
int n,A,B;
int minn[MAXN][MAXN],maxx[MAXN][MAXN];
template<typename Comp>
void floyd(int dis[MAXN][MAXN],const Comp func)
{
for(int k=1;k<=n;k++)
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++)
if(i!=k && k!=j && i!=j)
dis[i][j]=func(dis[i][j],dis[i][k]+dis[k][j]);
}
template<typename Comp>
void solve(const Comp cmp)
{
int ans=0;
for(int i=1;i<=n;i++)
if(i!=A && i!=B)
for(int j=i+1;j<=n;j++)
if(j!=A && j!=B)
if(cmp(A,B,i,j))
ans++;
cout<<ans<<' ';
}
int main()
{
ios::sync_with_stdio(false);
cin>>n>>A>>B;
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++)
{
char ch;cin>>ch;
if(ch=='+')
{
maxx[i][j]=2;
minn[i][j]=1;
}
if(ch=='-')
{
maxx[i][j]=-1;
minn[i][j]=-2;
}
if(ch=='?')
{
maxx[i][j]=2;
minn[i][j]=-2;
}
}
floyd(maxx,[&](cint a,cint b){return a<b?a:b;});
floyd(minn,[&](cint a,cint b){return a>b?a:b;});
solve([&](cint a,cint b,cint c,cint d)
{
return minn[a][c]>maxx[d][b] || minn[a][d]>maxx[c][b];
});
solve([&](cint a,cint b,cint c,cint d)
{
return (minn[a][c]==maxx[d][b] && maxx[a][c]==minn[d][b] && minn[d][b]==maxx[d][b])
|| (minn[a][d]==maxx[c][b] && maxx[a][d]==minn[c][b] && minn[c][b]==maxx[c][b]);
});
solve([&](cint a,cint b,cint c,cint d)
{
return maxx[a][c]<minn[d][b] || maxx[a][d]<minn[c][b];
});
cout<<endl;
return 0;
}