CF727F [Polycarp's problems] & [EX_Polycarp's problems]

GreenDuck發表於2019-03-26

原題題意

給出長度為n的有序陣列,m次詢問,每次給出一個正整數x。你要刪除陣列中最少的元素,使得陣列中的字首和+x都為非負整數。允許離線,n≤750,m≤200,000。


 

原題思路

首先注意到,x能成功通過測試當且僅當字首和中最小的數≥x。

將詢問從大到小排個序,對於一個新的詢問,每次嘗試從陣列中刪除最優的一個數,使得成功的機會更大。

何為最優?我們注意到,ai只會對後面的數造成影響。設當前字首和最小為now,fi為前i個字首和中最小的數,則答案會增加 max { min { now-ai , fi-1 } } (請注意後面的f囊括了now-ai顧及不到的情況)。

每次判斷最小的數在哪,暴力更新,O(n3+mlogm)。


 

程式碼

 1 #pragma GCC optimize(2)
 2 #include<bits/stdc++.h> 
 3 using namespace std;
 4 typedef long long int ll;
 5 const ll inf=1000000000000000000;
 6 const ll maxn=1E6+5;
 7 template<typename T> void read(T &x){
 8     x=0;char ch=getchar();int fh=1;
 9     while (ch<'0'||ch>'9'){if (ch=='-')fh=-1;ch=getchar();}
10     while (ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
11     x=x*fh;
12 }
13 void write(ll x)
14 {
15     if(x==0){putchar('0');putchar('\n');return;}
16     if(x<0){putchar('-');x=-x;}
17     ll a[25],size=0;
18     while(x){a[++size]=x%10;x/=10;}
19     for(int i=size;i>=1;--i)putchar(a[i]+'0');
20     putchar('\n');
21 }
22 ll min(ll x,ll y){return x<y?x:y;}
23 ll max(ll x,ll y){return x>y?x:y;}
24 ll n,m,a[maxn],tot,sum[maxn],ans[maxn],f[maxn];
25 struct query{ll pos,x;}Q[maxn];
26 bool cmp(query a,query b){return a.x>b.x;}
27 ll get()
28 {
29     ll ans=inf;
30     for(int i=1;i<=n;++i)
31     {
32         sum[i]=sum[i-1]+a[i];
33         ans=min(ans,sum[i]);
34         f[i]=min(f[i-1],sum[i]);
35     }
36     return ans;
37 }
38 int main()
39 {
40     read(n);read(m);
41     for(int i=1;i<=n;++i)
42     {
43         read(a[i]);
44         if(a[i]<0)++tot;
45     }
46     for(int i=1;i<=m;++i)
47     {
48         read(Q[i].x);
49         Q[i].pos=i;
50     }
51     sort(Q+1,Q+m+1,cmp);
52     int pos=1;
53     for(int i=1;i<=tot;++i)
54     {
55         ll g=get();
56         while(Q[pos].x+g>=0&&pos<=m){ans[Q[pos].pos]=i-1;++pos;}
57         if(g>=0)break;
58         ll ans=-inf,pos=0;
59         for(int j=1;j<=n;++j)
60         {
61             if(min(g-a[j],f[j-1])>ans)
62             {
63                 ans=min(g-a[j],f[j-1]);
64                 pos=j;
65             }
66         }
67         a[pos]=0;
68     }
69     ll g=get();
70     while(Q[pos].x+g>=0&&pos<=m){ans[Q[pos].pos]=tot;++pos;}
71     for(int i=1;i<=m;++i)write(ans[i]);
72     return 0;
73 }
View Code

 

 

 

 

 

 

EX版本:

m,n≤1,000,000,強制線上。


思路

考慮從後往前貪心。我們先只考慮兩種情況(0顯然沒必要考慮)。

第一種,所有的數均為負數。則每次答案必然會從最小的數刪起,直到刪的數的絕對值第一次大於等於詢問。

第二種,只有一個數為正,其餘均為負。兩種情況:

       第一種,加起來為仍為正,那麼就不會刪數。並且這一位不會對以後造成任何影響(既然都加上正數了,能到達這一位,以後肯定不會小於零)。

  第二種,加起來為負。那麼會貪心地選擇最小的負數刪,直到加起來為正。換個角度,會貪心地選擇最大的負數加到正數上,直到正數為負。這樣,化歸為第一情況。

       再將負數加入大根堆,正數不要的原因同第一種。

圖畫畫,手算算至少能夠理解。

最後得到的答案要麼剩下正數,要麼全是負數。這些負數取反後,從大到小排序的第i位代表了取到第i種答案,要刪去1~i個數。

二分查詢即可。


 

程式碼

 1 #pragma GCC optimize(2)
 2 #include<bits/stdc++.h> 
 3 using namespace std;
 4 typedef long long int ll;
 5 const ll inf=1000000000000000000;
 6 const ll maxn=1E6+5;
 7 ll min(ll x,ll y){return x<y?x:y;}
 8 ll max(ll x,ll y){return x>y?x:y;}
 9 template<typename T> void read(T &x){
10     x=0;char ch=getchar();int fh=1;
11     while (ch<'0'||ch>'9'){if (ch=='-')fh=-1;ch=getchar();}
12     while (ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
13     x=x*fh;
14 }
15 void write(ll x)
16 {
17     if(x==0){putchar('0');putchar('\n');return;}
18     if(x<0){putchar('-');x=-x;}
19     ll a[25],size=0;
20     while(x){a[++size]=x%10;x/=10;}
21     for(int i=size;i>=1;--i)putchar(a[i]+'0');
22     putchar('\n');
23 }
24 ll n,m,a[maxn],wait[maxn],size,x;
25 priority_queue<ll>Q;
26 int main()
27 {
28     read(n);read(m);
29     for(int i=1;i<=n;++i)read(a[i]);
30     for(int i=n;i>=1;--i)
31     {
32         while(a[i]>=0&&!Q.empty())
33         {
34             a[i]+=Q.top();
35             Q.pop();
36         }
37         if(a[i]<0)Q.push(a[i]);
38     }
39     while(!Q.empty())
40     {
41         wait[++size]=Q.top();
42         Q.pop();
43     }
44     for(int i=1;i<=size/2;++i)swap(wait[i],wait[size-i+1]);
45     for(int i=1;i<=size;++i)wait[i]+=wait[i-1];
46     for(int i=1;i<=size;++i)wait[i]=-wait[i];
47     while(m--)
48     {
49         read(x);
50         write(lower_bound(wait,wait+size+1,wait[size]-x)-wait);
51     }
52     return 0;
53 }
View Code

 

相關文章