ICPC 2018北京賽站網路賽題目4 : 80 Days+迴圈陣列動態維護字首和
題目連結:http://hihocoder.com/problemset/problem/1831 題目大意: 世界各地的一個圓圈上有n個城市,它們按照圓圈上的順序從1到n編號。當你到達這個城市會得到ai塊錢,從這個城市去另外的城市要花費bi塊錢,這個遊戲的目標是選擇一個城市作為起點,一開始你有c美元,然後沿著圓圈去一次訪問整個城市,最後回到起點。在旅途中,你的錢必須不低於零。 這裡有一個問題:要完成這次旅行,您會選擇哪個城市作為開始城市? 如果有多個答案,請輸出編號最小的答案。 如果沒有答案, 輸出-1
輸入的第一行是一個整數T(T ≤100),的測試案例數量。 對於每一個測試的情況下,第一行包含兩個整數N和C(1≤N ≤10^6, 0≤ C≤10^9)。 第二行包含N個整數ai(- 10^9 ≤ ai ≤10^9), 第三行包含N個整數bi…bn(0≤ bn ≤10^9)。
保證所有測試用例中n的總和小於10^6
思考: 因為到一個城市先得到錢,所以經過一個城市得到的錢為a[i]-b[i]; 圖中ai=a[i]-b[i];
首先求字首和,判斷s[n]+m>=0,如果成立則可以訪問全部城市。 這時列舉起始城市,如果以1為開始城市,則是s[i]+m,…,s[n]+m都要>=0。 如果以2為開始城市,則以2為起始城市的n個城市的字首和都要>=0。 … 以此類推
如果每列舉一個起點,求一次字首和,複雜度為O(T*n^n),肯定會T。
那麼我們可以動態維護字首和。 假如以2為起點時,除a1的字首和,其他的字首和都-=a1。而s[1]=(a[1]-b[1])+s[n-1]; 假如以3為起點時,除a2的字首和,其他的字首和都在以2為起點的基礎上-=a2。而s[2]=(a[2]-b[2])+s[1]; … 以此類推
那麼所有的-=可以用s3記錄下來,那麼每次只要維護s[i-1]就行了。 用multiset儲存字首和,那麼每次找min字首和的複雜度為log n。 整體的複雜度為O(T* n*log n)
思考:當時T了很多次,後來發現是陣列開小了。。。。。,怎麼不是MLE。。。。 話說當時還想到一種O(n)的思路,不過沒有驗證過是否正確。
記錄第一次的字首和的最小值S(min),和下標j,在每次列舉起點i時,除S[i-1]外,所有的字首和都減去相同的數,所以S(min)=min( S(min), S[i-1] )
如果S[min]的下標為j, 則所以的列舉i<=j都沒使S[min]-s3+m>=0, 則起點為j+1(沒有證明)
剛才再看了當時的程式碼,發現維護字首和的地方,維護的是S[i]的字首和,竟然AC。。。。。資料太水???
#include<bits/stdc++.h>
using namespace std;
int a[1000005];
int b[1000005];
long long s[1000005];
int main()
{
int t;
scanf("%d",&t);
while(t--)
{
multiset<long long> st;
int n, m;
scanf("%d%d",&n,&m);
for(int i=0;i<n;i++)
{
scanf("%d",&a[i]);
}
for(int i=0;i<n;i++)
{
scanf("%d",&b[i]);
}
s[0]=a[0]-b[0];
long long s1=s[0];
for(int i=0;i<n;i++)
{
s[i]=s[i-1]+a[i]-b[i];//字首和
st.insert(s[i]);
}
s1=s[n-1]+m;
if(s1>=0)//如果成立則可以訪問全部城市。
{
long long s2=*st.begin();//字首和的最小值
long long s3=0;
if(s2+m>=0) //第一個城市
{
printf("1\n");
}
else
{
for(int i=1;i<n;i++)
{
s3+=(a[i-1]-b[i-1]);//記錄s3
st.erase(st.find(s[i-1]));
if(i==1)
{
s[0]=(a[0]-b[0])+s[n-1];//i="2"特殊維護
st.insert(s[0]);
}
else
{
s[i-1]=(a[i-1]-b[i-1])+s[i-2];//維護字首和
st.insert(s[i-1]);
/*
寫成
s[i]=(a[i]-b[i])+s[i-1];
st.insert(s[i]);
也能AC
資料比較水???
*/
}
s2=*st.begin();
if(s2+m>=s3)//判斷是否滿足條件
{
printf("%d\n",i+1);
break;
}
}
}
}
else
printf("-1\n");
}
return 0;
}