[SRM553-1000]YamanoteLine

题目大意

有一个n个点的环,相邻两个点距离是正整数。
现在有若干个约束,是以下两种其中之一:
1、S与T的顺时针距离不小于L。
2、S与T的顺时针距离不大于L。
问环总长的解的方案数。

差分约束

不妨设D[i]表示0与i的顺时针距离,特殊的,D[n]表示整个环的总长,设为X。
显然,D[i+1]D[i]>=1,等同于D[i]D[i+1]1
D[n]D[0]=X,拆成D[n]D[0]XD[0]D[n]X
对于给定约束,显然也可以写成D[a]D[b]的形式。
于是我们可以建立一幅图,最短路对应一种约束最强的解,因此只有每个点均存在最短路即有解。
有些边权是未知数X,我们于是知道如何判定一个X是否合法,即原图是否产生负环,可以用最短路算法判定。
那么我们如何求有多少个X合法呢?

二分

显然,X的取值范围是一段区间。
说明是可二分的,难点是如何知道一个非法X是大了还是小了。
最短路算法可以获取图上一个负环关于X的系数和,只要知道其是正还是负,便知道X是大了还是小了。

Floyd

注意到每条边关于X的系数在[-1,1]内,合法情况下最短路不可能走重复边,可以发现任意一条路径关于X的系数和是O(m)级别。
不妨设f[i,j,k]表示从i到j的一条路径,路径上关于X的系数和为k,常数项最小是多少。
求出这个后,只要能保证任意f[i,i,s]+sX>=0即可,因此可以通过这个求出X的取值上下界。

#include<cstdio>
#include<algorithm>
#define fo(i,a,b) for(i=a;i<=b;i++)
using namespace std;
typedef long long ll;
const int maxn=50+10;
const ll inf=10000000000000000;
ll f[maxn][maxn][200+10];
int i,j,k,l,r,s,t,x,y,n,m,tot,mx;
ll L,R;
class YamanoteLine{
    void link(int x,int y,int kk,int bb){
        f[x][y][kk+mx]=min(f[x][y][kk+mx],(ll)bb);
    }
    public:
    ll howMany(int N,vector<int> S1,vector<int> T1,vector<int> L1,vector<int> S2,vector<int> T2,vector<int> L2){
        n=N;
        mx=n;
        fo(i,0,n)
            fo(j,0,n)
                fo(k,0,mx)
                    f[i][j][k]=inf;
        fo(i,0,n) f[i][i][mx]=0;
        fo(i,0,n-1) link(i+1,i,0,-1);
        link(n,0,-1,0);
        link(0,n,1,0);
        r=S1.size()-1;
        fo(i,0,r){
            s=S1[i];t=T1[i];l=L1[i];
            if (s<t) link(t,s,0,-l);
            else link(t,s,1,-l);
        }
        r=S2.size()-1;
        fo(i,0,r){
            s=S2[i];t=T2[i];l=L2[i];
            if (s<t) link(s,t,0,l);
            else link(s,t,-1,l);
        }
        fo(k,0,n)
            fo(i,0,n)
                fo(x,-mx,mx)
                    if (f[i][k][x+mx]<inf)
                        fo(j,0,n)
                            fo(y,max(-mx,-mx-x),min(mx,mx-x))
                                if (f[k][j][y+mx]<inf)
                                    f[i][j][x+y+mx]=min(f[i][j][x+y+mx],f[i][k][x+mx]+f[k][j][y+mx]);
        fo(i,0,n)
            if (f[i][i][mx]<0) return 0;
        L=n;R=inf;
        fo(s,1,mx)
            fo(i,0,n) 
                if (f[i][i][s+mx]<inf) L=max(L,(ll)(-f[i][i][s+mx]-1)/s+1);
        fo(s,-mx,-1)
            fo(i,0,n)
                if (f[i][i][s+mx]<inf) R=min(R,(ll)f[i][i][s+mx]/(-s));
        if (L>R) return 0;
        else if (R==inf) return -1;
        else return R-L+1;
    }
};
阅读更多
版权声明:本文是蒟蒻写出来的,神犇转载也要说一声哦! https://blog.csdn.net/WerKeyTom_FTD/article/details/79390186
上一篇GDKOI2018终焉记&WC2018并列记
下一篇[SRM554-500]TheBrickTowerMediumDivOne
想对作者说点什么? 我来说一句

没有更多推荐了,返回首页

关闭
关闭
关闭