BZOJ 3747: [POI2015]Kinoman 線段樹
阿新 • • 發佈:2017-10-17
hellip highlight for etc sha app pac += 你會
2 3 1 1 4 1 2 4 1
5 3 6 6
題目描述
共有m部電影,編號為1~m,第i部電影的好看值為w[i]。 在n天之中(從1~n編號)每天會放映一部電影,第i天放映的是第f[i]部。 你可以選擇l,r(1<=l<=r<=n),並觀看第l,l+1,…,r天內所有的電影。如果同一部電影你觀看多於一次,你會感到無聊,於是無法獲得這部電影的好看值。所以你希望最大化觀看且僅觀看過一次的電影的好看值的總和。輸入
第一行兩個整數n,m(1<=m<=n<=1000000)。 第二行包含n個整數f[1],f[2],…,f[n](1<=f[i]<=m)。 第三行包含m個整數w[1],w[2],…,w[m](1<=w[j]<=1000000)。輸出
輸出觀看且僅觀看過一次的電影的好看值的總和的最大值。樣例輸入
9 42 3 1 1 4 1 2 4 1
5 3 6 6
樣例輸出
15
樣例解釋:
觀看第2,3,4,5,6,7天內放映的電影,其中看且僅看過一次的電影的編號為2,3,4。
在沈隊的博客上看了這道題感覺不錯,在來做的這道題。
這道題,我認為主要有兩個難點:
1:
對於每一天,下一次該天播放的電影下次播放時間的處理。
2:
將電影對後面的影響轉換到線段樹上。
首先,我們定義線段樹為區間最大值。
我們將每次電影的價值的表達形式變為:
這次該種電影到下次該種電影的區間都加上w[i]。
這樣轉換之後,我們在枚舉每次播放電影的時候只需要取到1到n的區間中最大值,就是答案。
代碼:
#include<iostream> #include<cstdio> #include<cstdlib> #include<cstring> #include<algorithm> #define ll long long #define il inline #define db double #define max(a,b) ((a)>(b)?(a):(b)) #define min(a,b) ((a)<(b)?(a):(b)) using namespace std; il int gi() { int x=0,y=1; char ch=getchar(); while(ch<‘0‘||ch>‘9‘) { if(ch==‘-‘) y=-1; ch=getchar(); } while(ch>=‘0‘&&ch<=‘9‘) { x=x*10+ch-‘0‘; ch=getchar(); } return x*y; } il ll gl() { ll x=0,y=1; char ch=getchar(); while(ch<‘0‘||ch>‘9‘) { if(ch==‘-‘) y=-1; ch=getchar(); } while(ch>=‘0‘&&ch<=‘9‘) { x=x*10+ch-‘0‘; ch=getchar(); } return x*y; } struct node { int l,r; ll s; }c[4000045]; ll lazy[4000045]; il void pushdown(int rt) { if(lazy[rt]) { lazy[rt<<1]+=lazy[rt]; lazy[(rt<<1)+1]+=lazy[rt]; c[rt<<1].s+=lazy[rt]; c[(rt<<1)+1].s+=lazy[rt]; lazy[rt]=0; } } void add(int rt,int l,int r,int L,int R,ll num) { if(L<=l&&R>=r) { c[rt].s+=num; lazy[rt]+=num; return; } pushdown(rt); int mid=(l+r)>>1; if(L<=mid) add(rt<<1,l,mid,L,R,num); if(R>mid) add((rt<<1)+1,mid+1,r,L,R,num); c[rt].s=max(c[rt<<1].s,c[(rt<<1)+1].s); } int next[1000045];//i th day next time to show int day[1000045];//num i film last time appear int f[1000045]; ll w[1000045]; int main() { int n=gi(),m=gi(); for(int i=1;i<=n;i++) f[i]=gi(); for(int i=1;i<=m;i++) w[i]=gl(); for(int i=n;i>=1;i--)//必須逆向才能求出來 { next[i]=day[f[i]]; day[f[i]]=i; } for(int i=1;i<=m;i++)//把每種電影第一段區間加上 { if(!day[i]) continue; if(next[day[i]]) add(1,1,n,day[i],next[day[i]]-1,w[i]); else//只出現了一次 add(1,1,n,day[i],n,w[i]); } ll ans=0; for(int i=1;i<=n;i++) { ans=max(ans,c[1].s); if(next[i]) { add(1,1,n,i,next[i]-1,-w[f[i]]);//把當前到下次出現的區間減去 if(next[next[i]])//為枚舉到next[i]做準備 add(1,1,n,next[i],next[next[i]]-1,w[f[i]]); else add(1,1,n,next[i],n,w[f[i]]); } else add(1,1,n,i,n,-w[f[i]]); } printf("%lld\n",ans); return 0; }
BZOJ 3747: [POI2015]Kinoman 線段樹