1. 程式人生 > >【BZOJ1122】[POI2008] 賬本BBB

【BZOJ1122】[POI2008] 賬本BBB

→傳送門←

正解: 貪心單調佇列優化

先貼上一張別人寫的被老師發下來給我們的題解(就是看著這張題解才寫出來的)

 

下面是自己的話(一些具體操作過程):

把環拆成一條2*n的鏈,然後用優先佇列來求出每一個區間的最小字首和(先不考慮p),存在了fM[]裡面。

然後列舉起點(即 第二次操作的使用次數)算出此時的費用cost去更新ans。

要注意的是,如果此時我需要將幾個加號改成減號,按貪心的思路就是將最後幾個加號改掉,這樣的話,是不會影響這個區間的最小字首和的(為什麼呢?這個應該很好想吧),因為最小字首和一定會以負號作為結尾,而Q是大於等於零的,如果這時改掉最後幾個加號會使字首和變負數的話,怎麼可能最終加到Q呢?

程式碼~

 1 #include<iostream>
 2 #include<cstdio>
 3 #include<cstdlib>
 4 #include<cstring>
 5 #include<algorithm>
 6 
 7 #define For(i,a,b) for(register int i=a;i<=b;++i)
 8 #define Dwn(i,a,b) for(register int i=a;i>=b;--i)
 9 #define Re register
10 
11
using namespace std; 12 const int N=1e6+10; 13 int s[N*2],a[N*2]; 14 struct Qr{ 15 int x,st; 16 }q[N*4]; 17 int n,m,x,y,Q,P,fM[N*2],cost,ans=2147483600; 18 inline void read(int &v){ 19 v=0; 20 char c=getchar(); 21 while(c<'0'||c>'9')c=getchar(); 22 while(c>='0'&&c<='
9')v=v*10+c-'0',c=getchar(); 23 } 24 int main(){ 25 read(n); read(P); read(Q); read(x); read(y); 26 For(i,1,n){ 27 char c=getchar(); 28 while(c!='-'&&c!='+')c=getchar(); 29 if(c=='-')a[i]=a[i+n]=-1; 30 else a[i]=a[i+n]=1; 31 } 32 For(i,1,n*2)s[i]=s[i-1]+a[i]; 33 34 int f,r; 35 f=1; r=0; 36 Dwn(i,n*2-1,1){ 37 int rx=i+n-1; 38 while(f<=r&&q[f].st>rx)f++; 39 while(f<=r&&q[r].x>=s[i])r--; 40 q[++r].x=s[i]; q[r].st=i; 41 if(i<=n)fM[i]=q[f].x-s[i-1]; 42 } 43 int nd=(Q-P-s[n])/2; 44 45 Dwn(i,n+1,2){ 46 cost=y*(n+1-i)+abs(nd)*x; 47 48 int Ms; 49 if(i==n+1)Ms=fM[1]; 50 else Ms=fM[i]; 51 52 if(nd>=0)Ms+=P+nd*2; 53 else Ms+=P; 54 55 if(Ms<0)cost+=2*x*((1-Ms)/2); 56 ans=min(ans,cost); 57 } 58 cout<<ans<<endl; 59 }