原题题意

给出长度为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

 

 posted on 2019-03-26 21:09  GreenDuck  阅读(191)  评论(1编辑  收藏  举报