【環形線性區間dp】[CF]E. Petya and Post
【環形線性區間dp】[CF]E. Petya and Post
題意(稍微變型):有n個加油站,編號為1到n,且每個加油站都能提供\(a_i\)的油量(只能提供一次),並且從一個加油站到相鄰的加油站會消耗一定量的油量。求在這n個加油站中有幾個加油站可以滿足順時針或者逆時針地整整跑完一圈?
思路:如果中途一旦出現油量小於0的情況(假設真的能)就說明車子停到一半,就沒有繼續跑了。
用dp[i]
表示從i點出發且最終又回到終點這樣的路線中間出現了的最小值(這是一個非常重要且起決定作用的因素)
先單單從順時針方向進行考慮,從點i+1出發和從點i出現的不同之處在於從i+1點出發的所有過程脫去先從i點取到油並且耗費一定油最終剩餘或者虧欠的油量的影響,而,而脫去某種影響意思就是要去做一下修正操作,而這個修正操作也會直接影響到中途出現的最小值(最危險的情況),如果是
\(D_1=a_1-b_1\), (第i個點出發到i+1點的且沒在i+1點加油的情況)
\(D_2=(a_1+a_2)-(b_1+b_2)\), (第i個點出發到i+2點的且沒在i+2點加油的情況)
\(D_3=(a_1+a_2+a_3)-(b_1+b_2+b_3)\),
…
\(Dn=(a_1+a_2+…+a_n)-(b_1+b_2+…+b_n)\)
令\(D_{min}=min\{D_i\}\)
\(E_1=a_2-b_2=D_2-(a_1-b_1)\),
\(E_2=(a_2+a_3)-(b_2+b_3)=D_3-(a_1-b_1)\),
\(E_3=(a_2+a_3+a_4)-(b_2+b_3+b_4)=D_4-(a_1-b_1)\)
…
\(E_{n-1}=(a_2+…+a_n)-(b_2+…+b_n)=D_n-(a_1-b_1)\)
\(En=(a_2+…+a_n+a_1)-(b_2+…+b_n+b_1)\)
可以建立座標系,將E和D的函式繪製出來,發現E的影象基本上是D的影象左移一個單位並向下平移\((a_1-b_1)\)繪製出來,也就是說最小值的位置是沒有發生變化的
令\(E_{min}=min\{E_i\}=D_{min}-(a_1-b_1)\)
\(F_1=E_2-(a_2-b_2)\)
...
\(F_{n-1}=(a_3+…+a_n+a_1)-(b_3+…+b_n+b_1)=E_n-(a_2-b_2)\)
同理,可以建立座標系,將E和D的函式繪製出來,發現E的影象基本上是D的影象左移一個單位並向下平移\((a_2-b_2)\)繪製出來,也就是說最小值的位置是沒有發生變化的
#include <bits/stdc++.h>
#define MEM(a,x) memset(a,x,sizeof(a))
#define W(a) while(a)
#define gcd(a,b) __gcd(a,b)
#define pi acos(-1.0)
#define PII pair<int,int>
#define pb push_back
#define mp make_pair
#define fi first
#define se second
#define ll long long
#define ull unsigned long long
#define rep(i,x,n) for(int i=x;i<n;i++)
#define repd(i,x,n) for(int i=x;i<=n;i++)
#define MAX 1000005
#define MOD 1000000007
#define INF 0x3f3f3f3f
#define lowbit(x) (x&-x)
using namespace std;
ll gcd(ll a, ll b) {return b ? gcd(b, a % b) : a;}
ll lcm(ll a, ll b) {return a / gcd(a, b) * b;}
inline int read()
{
int x=0,f=1;
char ch=getchar();
while(ch<'0'||ch>'9')
{
if(ch=='-')
f=-1;
ch=getchar();
}
while(ch>='0' && ch<='9')
x=x*10+ch-'0',ch=getchar();
return x*f;
}
const int N = 1E5+5;
int n,m,dp[N];
bool ans[N];
int main()
{
n=read();
int A[n+1],B[n+1];
for(int i=1;i<=n;i++)
A[i] = read();
for(int i=1;i<=n;i++)
B[i] = read();
int tmp=0;
for(int i=1;i<=n;i++)
{
tmp+=A[i]-B[i];
dp[1]=min(dp[1],tmp);
}
if(dp[1]>=0) ans[1]=1;
for(int i=2;i<=n;i++)
{
dp[i]=dp[i-1]-(A[i-1]-B[i-1]);
if(dp[i]>=0) ans[i]=1;
}
//逆時針相當於把第n個點倒一下當成起點
tmp=0,B[0]=B[n];
memset(dp,0,sizeof(dp));
for(int i=n;i>=1;i--)
{
tmp+=A[i]-B[i-1];
dp[n]=min(dp[n],tmp);
}
if(dp[n]>=0) ans[n]=1;
for(int i=n-1;i>=1;i--)
{
dp[i]=dp[i+1]-(A[i+1]-B[i]);
if(dp[i]>=0) ans[i]=1;
}
vector<int> vec;
for(int i=1;i<=n;i++)
if(ans[i]) vec.push_back(i);
sort(vec.begin(),vec.end());
cout<<vec.size()<<endl;
for(int i=0;i<vec.size();i++)
{
if(i!=0) cout<<" ";
cout<<vec[i];
}
return 0;
}