ARC117 - Gateau 题目大意:给定一个长度为$2n$的非负环序列$x_0,x_1,\cdots x_{2n-1}$,以及$2n$条限制,每条都是
$\forall A_i,\sum_{j=0}^{n-1} x_{i+j\mod 2n}\ge A_i $
求最小化$\sum x_i$
转化为前缀和作差之后,令人联想到差分约束,但是难以处理跨过环末的限制
于是二分答案$s=x_{2n-1}$,建立最长路图
$\forall i<n,dis_{i+n}\ge dis_{i}+A_i$
$\forall i\ge n,dis_{i-n}\ge dis_{i}+A_i-s$
$\forall i<2n-1,dis_{i+1}\leq dis_i$
那么无解的条件就是:存在正环或者求得$dis_{2n-1}>s$
自然无法直接通过$\text{SPFA}$来跑。。。
考虑所有的边构成了一条$0-2n-1$的零链 和若干极小的二元环
如果二元环出现正环则无解,否则任意一条最长路路径总是可以描述为
$i<j<n , i(+n)\rightarrow j(+n)$
在中间点$k$可以选择花费0的代价向后走,或者
$k<n:k\rightarrow k+n,cost=A_k$
$k\ge n:k\rightarrow k-n,cost=A_k-s$
也就是在$k,k+n$之间反复横跳,由此发现一条路径就是
从$0-n-1$进行扫描,并且允许中间$\pm n$横跳
(当然这里漏掉了一个特殊边,即$dis_{n}\ge dis_{n-1}$,这是构成环的边)
这样写出一个变种的$\text{Bellman Ford}$,由于图的特殊性,只需要常数轮即可确定正环
具体的,当图上不存在正环时,扫描最多经过一次环就会停止更新
也就是这样横跳的扫描更新只会进行常数轮(2轮?)
如果若干轮后依然在更新,说明出现了正环
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 #include <bits/stdc++.h> using namespace std;typedef long long ll;typedef unsigned long long ull;typedef double db;typedef pair <int ,int > Pii;#define reg register #define mp make_pair #define pb push_back #define Mod1(x) ((x>=P)&&(x-=P)) #define Mod2(x) ((x<0)&&(x+=P)) #define rep(i,a,b) for(int i=a,i##end=b;i<=i##end;++i) #define drep(i,a,b) for(int i=a,i##end=b;i>=i##end;--i) template <class T > inline void cmin (T &a,T b) { ((a>b)&&(a=b)); }int cmax (int &a,int b) { return a<b?a=b,1 :0 ; }char IO;template <class T =int > T rd () { T s=0 ; int f=0 ; while (!isdigit (IO=getchar ())) f|=IO=='-' ; do s=(s<<1 )+(s<<3 )+(IO^'0' ); while (isdigit (IO=getchar ())); return f?-s:s; } const int N=3e5 +10 ,INF=1e9 +10 ;int n;int A[N],dp[N];int Check (int s) { rep (i,0 ,n-1 ) if (A[i]+A[i+n]-s>0 ) return 0 ; rep (i,0 ,n*2 -1 ) dp[i]=0 ; dp[n*2 -1 ]=s; int f=0 ; rep (k,0 ,5 ) { f=0 ; rep (i,0 ,n-1 ) { f|=cmax (dp[i+n],dp[i]+A[i]); f|=cmax (dp[i],dp[i+n]+A[i+n]-s); if (i<n-1 ) { f|=cmax (dp[i+1 ],dp[i]); f|=cmax (dp[i+n+1 ],dp[i+n]); } } f|=cmax (dp[n],dp[n-1 ]); } if (f || dp[n*2 -1 ]>s) return 0 ; return 1 ; } int main () { n=rd (); rep (i,0 ,n*2 -1 ) A[i]=rd (); int res=-1 ; for (int l=0 ,r=1.05e9 ,mid;l<=r;) Check (mid=(l+r)>>1 )?r=mid-1 ,res=mid:l=mid+1 ; printf ("%d\n" ,res); }