Kinoman bzoj-3747 POI-2015
题目大意:有m部电影,第i部电影的好看值为w[i]。现在放了n天电影,请你选择一段区间l~r使得l到r之间的好看值总和最大。特别地,如果同一种电影放了两遍及以上,那么这种电影的好看值将不会被获得。
注释:$1le m le n le 10^6$。
想法:和rmq problem类似的,我们处理出每一个位置pos右边第一个和pos上电影种类相同的位置nxt[pos]。然后,我从1-n扫一遍,每次讲l+1到nxt[l]-1之间的值加上w[a[l]],这个过程可以用线段树维护。
最后,附上丑陋的代码... ...
#include <stdio.h> #include <string.h> #include <algorithm> using namespace std; #define N 1000050 #define lson pos<<1 #define rson pos<<1|1 typedef long long ll; ll t[N<<2],lazy[N<<2]; int f[N],w[N],nxt[N],n,m,now[N]; char nc() { static char buf[100000],*p1,*p2; return p1==p2&&(p2=(p1=buf)+fread(buf,1,100000,stdin),p1==p2)?EOF:*p1++; } inline int rd() { register int x=0; register char s=nc(); while(s<'0'||s>'9')s=nc(); while(s>='0'&&s<='9')x=(x<<3)+(x<<1)+s-'0',s=nc(); return x; } void pushdown(int pos) { if(!lazy[pos]) return; ll d=lazy[pos]; lazy[lson]+=d; lazy[rson]+=d; t[lson]+=d; t[rson]+=d; lazy[pos]=0; } void update(int l,int r,int x,int y,ll v,int pos) { if(x<=l&&y>=r) { t[pos]+=v; lazy[pos]+=v; return; } pushdown(pos); int mid=(l+r)>>1; if(x<=mid) update(l,mid,x,y,v,lson); if(y>mid) update(mid+1,r,x,y,v,rson); t[pos]=max(t[lson],t[rson]); } int main() { n=rd(),m=rd(); for(int i=1;i<=n;i++) { f[i]=rd(); } for(int i=1;i<=m;i++) { w[i]=rd(); } for(int i=n;i;i--) { nxt[i]=now[f[i]]; now[f[i]]=i; } for(int i=1;i<=m;i++) { if(now[i]) { if(!nxt[now[i]]) update(1,n,now[i],n,w[i],1); else update(1,n,now[i],nxt[now[i]]-1,w[i],1); } } ll ans=0; for(int i=1;i<=n;i++) { ans=max(ans,t[1]); if(nxt[i]) { update(1,n,i,nxt[i]-1,-w[f[i]],1); if(nxt[nxt[i]]) update(1,n,nxt[i],nxt[nxt[i]]-1,w[f[i]],1); else update(1,n,nxt[i],n,w[f[i]],1); } else update(1,n,i,n,-w[f[i]],1); } printf("%lld ",ans); return 0; }
小结:线段树能干好多事情啊qwq