bzoj2288【POJ Challenge】生日礼物*
题意:
给一个序列,求不超过m个连续的部分,使元素和最大。序列大小≤100000
题解:
先把连续的正数和负数合并起来,接着如果正数个数小于m则全选,否则需要确定去掉那个正数或合并哪个正数。初始ans设为所有正数和,将所有的数按绝对值大小放入堆中,然后重复m-正数个数操作:每次选取绝对值最小的数,如果是负数且它在边界处则重新选,否则将这个数删除并将两边的数合并,同时ans-=该数绝对值。该操作的意义在于:如果删去的是正数表示不选它,否则表示选这个负数以将左右的的正数合并起来。类似于1150,我用链表和STLset维护这个过程。
代码:
1 #include <cstdio> 2 #include <cstring> 3 #include <algorithm> 4 #include <set> 5 #define inc(i,j,k) for(int i=j;i<=k;i++) 6 #define maxn 100010 7 using namespace std; 8 9 inline int read(){ 10 char ch=getchar(); int f=1,x=0; 11 while(ch<'0'||ch>'9'){if(ch=='-')f=-1; ch=getchar();} 12 while(ch>='0'&&ch<='9')x=x*10+ch-'0',ch=getchar(); 13 return f*x; 14 } 15 int n,m,a[maxn],b[maxn],tot,ans; 16 struct sn{ 17 int pos,v; 18 bool operator < (const sn &x)const{return v!=x.v?v<x.v:pos<x.pos;} 19 }; 20 set<sn>s; 21 struct nd{int v,l,r; bool bit;}; nd nds[maxn]; 22 int main(){ 23 n=read(); m=read(); inc(i,1,n)b[i]=read(); 24 int last=0; inc(i,1,n){ 25 if(b[i]==0)continue; if(!last||(last>0^b[i]>0))a[++tot]=b[i];else a[tot]+=b[i]; last=b[i]; 26 } 27 n=tot; tot=0; inc(i,1,n)if(a[i]>0)tot++,ans+=a[i]; 28 if(tot>m){ 29 inc(i,1,n){s.insert((sn){i,abs(a[i])}); nds[i]=(nd){abs(a[i]),i-1,i+1>n?0:i+1,a[i]>0};} 30 inc(i,1,tot-m){ 31 set<sn>::iterator xx; int x; 32 while(1){ 33 xx=s.begin(); x=xx->pos; s.erase(xx); 34 if((!nds[x].l||!nds[x].r)&&!nds[x].bit){ 35 if(!nds[x].l)nds[nds[x].r].l=0; if(!nds[x].r)nds[nds[x].l].r=0; 36 }else break; 37 } 38 ans-=nds[x].v; 39 if(!nds[x].l&&!nds[x].r)continue; 40 else if(!nds[x].r)nds[nds[x].l].r=0; 41 else if(!nds[x].l)nds[nds[x].r].l=0; 42 else{ 43 nds[x].v=nds[nds[x].l].v+nds[nds[x].r].v-nds[x].v; nds[x].bit=nds[nds[x].l].bit; 44 xx=s.find((sn){nds[x].l,nds[nds[x].l].v}); s.erase(xx); 45 xx=s.find((sn){nds[x].r,nds[nds[x].r].v}); s.erase(xx); 46 s.insert((sn){x,nds[x].v}); 47 nds[x].l=nds[nds[x].l].l; nds[x].r=nds[nds[x].r].r; nds[nds[x].l].r=x; nds[nds[x].r].l=x; 48 } 49 } 50 } 51 printf("%d",ans); return 0; 52 }
20160905