#差分約束,Floyd#洛谷 2474 [SCOI2008]天平
阿新 • • 發佈:2020-11-04
分析
非傳統差分約束??
注意只有結果保證惟一的選法才統計在內
這就為差分約束提供了依據
以左邊重為例,假設現在選擇的砝碼為\(i,j\),
那麼\(\because A+B>i+j\therefore A-i>j-B\)
既然結果保證唯一,那麼也就是\(min(A-i)>max(j-B)\)
右邊重同理,也就是\(max(A-i)<min(j-B)\)
同樣重也很顯然,就是
\(min(A-i)=max(A-i),min(j-B)=max(j-B),max(A-i)=min(j-B)\)
講到這裡該怎麼做,floyd現身了,考慮維護最短路和最長路,
- 等號也就是\(i-j\leq 0,i-j\geq 0\)
- 大於號也就是\(i-j\leq -1,i-j\geq -2\)
- 小於號也就是\(i-j\leq 2,i-j\geq 1\)
- 問號也就是\(i-j\geq -2,i-j\leq 2\)
按照這個floyd建圖分別跑最短路和最長路即可
為什麼非傳統呢,是因為這個最短路和最長路僅僅是用來判斷上下界的
程式碼
#include <cstdio> #include <cctype> #define rr register using namespace std; const int N=51; char s[N]; int n,A,B,dmn[N][N],dmx[N][N],ans1,ans2,ans3; inline signed iut(){ rr int ans=0; rr char c=getchar(); while (!isdigit(c)) c=getchar(); while (isdigit(c)) ans=(ans<<3)+(ans<<1)+(c^48),c=getchar(); return ans; } inline signed min(int a,int b){return a<b?a:b;} inline signed max(int a,int b){return a>b?a:b;} signed main(){ scanf("%d%d%d",&n,&A,&B); for (rr int i=1;i<=n;++i){ scanf("%s",s+1),s[i]='='; for (rr int j=1;j<=n;++j) switch (s[j]){ case '=':{ dmn[i][j]=0,dmx[i][j]=0; break; } case '+':{ dmn[i][j]=1,dmx[i][j]=2; break; } case '-':{ dmn[i][j]=-2,dmx[i][j]=-1; break; } case '?':{ dmn[i][j]=-2,dmx[i][j]=2; break; } } } for (rr int k=1;k<=n;++k) for (rr int i=1;i<=n;++i) for (rr int j=1;j<=n;++j) if ((i^k)&&(i^j)&&(k^j)) dmn[i][j]=max(dmn[i][j],dmn[i][k]+dmn[k][j]), dmx[i][j]=min(dmx[i][j],dmx[i][k]+dmx[k][j]); for (rr int i=1;i<=n;++i) for (rr int j=1;j<i;++j) if ((i^j)&&(i^A)&&(i^B)&&(j^A)&&(j^B)){ if (dmn[A][i]>dmx[j][B]||dmn[B][i]>dmx[j][A]) ++ans1; if (dmn[i][A]>dmx[B][j]||dmn[i][B]>dmx[A][j]) ++ans3; if ((dmn[A][i]==dmx[A][i]&&dmn[j][B]==dmx[j][B]&&dmn[A][i]==dmx[j][B])|| (dmn[A][j]==dmx[A][j]&&dmn[i][B]==dmx[i][B]&&dmn[A][j]==dmx[i][B])) ++ans2; } return !printf("%d %d %d",ans1,ans2,ans3); }