Description
共有m部电影,编号为1~m,第i部电影的好看值为w[i]。
在n天之中(从1~n编号)每天会放映一部电影,第i天放映的是第f[i]部。
你可以选择l,r(1<=l<=r<=n),并观看第l,l+1,…,r天内所有的电影。如果同一部电影你观看多于一次,你会感到无聊,于是无法获得这部电影的好看值。所以你希望最大化观看且仅观看过一次的电影的好看值的总和。
Input
第一行两个整数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)。
Output
输出观看且仅观看过一次的电影的好看值的总和的最大值。
Sample Input
9 4
2 3 1 1 4 1 2 4 1
5 3 6 6
2 3 1 1 4 1 2 4 1
5 3 6 6
Sample Output
15
样例解释:
观看第2,3,4,5,6,7天内放映的电影,其中看且仅看过一次的电影的编号为2,3,4。
样例解释:
观看第2,3,4,5,6,7天内放映的电影,其中看且仅看过一次的电影的编号为2,3,4。
因为只有在区间中有且只有出现一次的电影才能统计,所以很容易想到要找出下一次出现同种电影的位置
令next[i]表示下次与第i天的电影相同的位置
从1到n枚举左端点,我们要确定右端点在那里的时候区间的权值最大
很容易发现有用的只有左端点下面某个电影第一次和第二次出现的位置,因为只有第一次出现之后才能加上权值,而在第二次出现之后这种电影就没有用了,随着右端点的继续移动这种电影的出现次数更不可能是1次
这样一来我们在枚举的时候可以用线段树维护一下当前左端点枚举到i,右端点在y的时候的区间的权值。
在枚举的时候如何转移?考虑选择区间[l,r]与区间[l+1,r]的不同,显然少了一个f[l]。那么f[l]下一次出现的地方是next[l],再下一次是next[next[l]]。所以区间[l,next[l]-1]之间都没有f[l]的电影存在了。同理区间[l,next[next[l]]-1]原本有出现两次f[l]不能统计答案,现在减去一次也要统计答案了。
线段树维护一下右端点在y的时候的区间权值,只要区间加法和区间最大值就可以了
#include<cstdio> #include<iostream> #define LL long long using namespace std; inline LL read() { LL x=0,f=1;char ch=getchar(); while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();} while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();} return x*f; } int w[1000010],f[1000010]; int lst[1000010],nxt[1000010]; int n,m; LL ans; struct segtree{int l,r;LL tag,mx;}tree[4000010]; inline void pushdown(int k) { if (tree[k].l==tree[k].r)return; LL tag=tree[k].tag;tree[k].tag=0; if (tag) { tree[k<<1].mx+=tag; tree[k<<1].tag+=tag; tree[k<<1|1].mx+=tag; tree[k<<1|1].tag+=tag; } } inline void buildtree(int now,int l,int r) { tree[now].l=l;tree[now].r=r; if (l==r)return; int mid=(l+r)>>1; buildtree(now<<1,l,mid); buildtree(now<<1|1,mid+1,r); } inline void add(int now,int x,int y,int d) { if (tree[now].tag)pushdown(now); int l=tree[now].l,r=tree[now].r; if (l==x&&r==y) { tree[now].tag+=d; tree[now].mx+=d; return; } int mid=(l+r)>>1; if (y<=mid)add(now<<1,x,y,d); else if (x>mid)add(now<<1|1,x,y,d); else { add(now<<1,x,mid,d); add(now<<1|1,mid+1,y,d); } tree[now].mx=max(tree[now<<1].mx,tree[now<<1|1].mx); } int main() { n=read();m=read(); for(int i=1;i<=n;i++)f[i]=read(); for(int i=1;i<=m;i++)w[i]=read(); for (int i=n;i>=1;i--) { nxt[i]=lst[f[i]]; lst[f[i]]=i; } buildtree(1,1,n); for(int i=1;i<=m;i++) if (lst[i]) { if (!nxt[lst[i]])add(1,lst[i],n,w[i]); else add(1,lst[i],nxt[lst[i]]-1,w[i]); } for (int i=1;i<=n;i++) { ans=max(ans,tree[1].mx); int to=nxt[i]; if (to) { add(1,i,to-1,-w[f[i]]); if (nxt[to])add(1,to,nxt[to]-1,w[f[i]]); else add(1,to,n,w[f[i]]); }else add(1,i,n,-w[f[i]]); } printf("%lld ",ans); return 0; }