1. 程式人生 > >洛谷P3582 [POI2015]KIN

洛谷P3582 [POI2015]KIN

題目描述

共有\(m\)部電影,編號為\(1——m\),第\(i\)部電影的好看值為\(w[i]\)。在\(n\)天之中(從\(1~n\)編號)每天會放映一部電影,第\(i\)天放映的是第\(f[i]\)部。你可以選擇\(l,r(1 \leq l \leq r \leq n)\),並觀看第\(l,l+1,…,r\)天內所有的電影。如果同一部電影你觀看多於一次,你會感到無聊,於是無法獲得這部電影的好看值。所以你希望最大化觀看且僅觀看過一次的電影的好看值的總和。

輸入輸出格式

輸入格式:

第一行兩個整數\(n,m(1 \leq m \leq n \leq 1000000)\)。第二行包含\(n\)個整數\(f[1],f[2],…,f[n]\)

。第三行包含\(m\)個整數\(w[1],w[2],…,w[m]\)

輸出格式:

輸出觀看且僅觀看過一次的電影的好看值的總和的最大值。

輸入輸出樣例

輸入樣例#1:

9 4
2 3 1 1 4 1 2 4 1
5 3 6 6

輸出樣例#1:

15

思路:這道題目我們可以考慮先記錄每種電影上一次開播時間和下一次開播時間(即以下程式碼中的\(last\)陣列和\(nxt\)陣列),然後對於每種電影,我們可以先處理中它是否播放過對後面區間的影響情況,然後再對\(n\)個時間點分別考慮,我們可以列舉左端點,然後根據左端點電影的播放情況就可以確定它可以影響到的最右端點,然後不斷更新,更新過程中記錄最大值,最後那個最大值即為答案。

洛谷P3582(自己寫的題解)

程式碼:

#include<cstdio>
#include<algorithm>
#include<cctype>
#define ll long long
#define maxn 1000007
#define ls rt<<1
#define rs rt<<1|1
using namespace std;
int n,m,f[maxn],nxt[maxn],last[maxn],a[maxn];
ll ans;
inline int qread() {            //快讀,不解釋……
  char c=getchar();int num=0,f=1;
  for(;!isdigit(c);c=getchar()) if(c=='-') f=-1;
  for(;isdigit(c);c=getchar()) num=num*10+c-'0';
  return num*f;
}
struct Tree {
  ll maxx,lazy;
}tree[maxn<<2];
inline void pushdown(int rt) {    //下放lazy標記。
  if(tree[rt].lazy) {
    tree[ls].lazy+=tree[rt].lazy;
    tree[rs].lazy+=tree[rt].lazy;
    tree[rs].maxx+=tree[rt].lazy;
    tree[ls].maxx+=tree[rt].lazy;
    tree[rt].lazy=0;
  }
}
void modify(int rt,int l,int r,int L,int R,int val) { //區間修改,用於後面的更新。
  if(L>r||R<l) return;
  if(L<=l&&r<=R) {
    tree[rt].lazy+=val;
    tree[rt].maxx+=val;
    return;
  }
  pushdown(rt);
  int mid=(l+r)>>1;
  modify(ls,l,mid,L,R,val),modify(rs,mid+1,r,L,R,val);
  tree[rt].maxx=max(tree[ls].maxx,tree[rs].maxx);
}
int main() {
  n=qread(),m=qread();
  for(int i=1;i<=n;++i) f[i]=qread();
  for(int i=1;i<=m;++i) a[i]=qread();
  for(int i=n;i>=1;--i) nxt[i]=last[f[i]],last[f[i]]=i; //處理出nxt和last陣列。
  for(int i=1;i<=m;++i) {
    if(last[i]) {                   //如果這個電影已經播放過。
      int zrj=nxt[last[i]];
      if(zrj) modify(1,1,n,last[i],zrj-1,a[i]);  
      //如果這不是最後一次播放這個電影,那麼可以影響到的最右端點是nxt[last[i]]-1,然後last[i]就是左端點,也是第一次看,所以在這個區間加上這個電影的價值。
      else modify(1,1,n,last[i],n,a[i]);  //如果是最後一次,那麼它將一直影響到最後。
    }
  }
  for(int i=1;i<=n;++i) {
    ans=max(ans,tree[1].maxx);         //每次更新一下最大值。
    int zrj=nxt[i];
    if(zrj) {                   //如果第二次播放。
      modify(1,1,n,i,zrj-1,-a[f[i]]);  //在這次和之後的一次的區間上價值減去這個電影的價值,因為相同電影看了價值為0。
      if(nxt[zrj]) modify(1,1,n,zrj,nxt[zrj]-1,a[f[i]]); //第二次和第三次之間加上這個電影的價值(因為是以第二次為左端點,只看了一次)。
      else modify(1,1,n,zrj,n,a[f[i]]); //不然就把第二次之後的加上這個價值。
    }
    else modify(1,1,n,i,n,-a[f[i]]);  //沒有第二次播放,就從當前時間開始一直到最後,減去這個價值。
  }
  printf("%lld\n",ans);
  return 0;
}