bzoj3747 [POI2015]Kinoman

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

Sample Output

15
样例解释:
观看第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\n",ans);
	return 0;
}
时间: 2024-11-07 06:07:01

bzoj3747 [POI2015]Kinoman的相关文章

【线段树】bzoj3747 [POI2015]Kinoman

题解:http://www.cnblogs.com/zyfzyf/p/4105184.html 1 #include<cstdio> 2 #include<algorithm> 3 #include<cmath> 4 using namespace std; 5 #define lson rt<<1,l,m 6 #define rson rt<<1|1,m+1,r 7 int Num,CH[12],f,c; 8 inline void R(int

【BZOJ3747】[POI2015]Kinoman 线段树

[BZOJ3747][POI2015]Kinoman 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&

[bzoj3747][POI2015]Kinoman_线段树

Kinoman bzoj-3747 POI-2015 题目大意:有m部电影,第i部电影的好看值为w[i].现在放了n天电影,请你选择一段区间l~r使得l到r之间的好看值总和最大.特别地,如果同一种电影放了两遍及以上,那么这种电影的好看值将不会被获得. 注释:$1\le m \le n \le 10^6$. 想法:和rmq problem类似的,我们处理出每一个位置pos右边第一个和pos上电影种类相同的位置nxt[pos].然后,我从1-n扫一遍,每次讲l+1到nxt[l]-1之间的值加上w[a

BZOJ 3747: [POI2015]Kinoman( 线段树 )

线段树... 我们可以枚举左端点 , 然后用线段树找出所有右端点中的最大值 . ----------------------------------------------------------------------------------------- #include<cstdio> #include<algorithm> #include<cstring> #include<iostream> #define rep( i , n ) for( i

【BZOJ-3747】Kinoman 线段树

3747: [POI2015]Kinoman Time Limit: 60 Sec  Memory Limit: 128 MBSubmit: 715  Solved: 294[Submit][Status][Discuss] Description 共有m部电影,编号为1~m,第i部电影的好看值为w[i]. 在n天之中(从1~n编号)每天会放映一部电影,第i天放映的是第f[i]部. 你可以选择l,r(1<=l<=r<=n),并观看第l,l+1,…,r天内所有的电影.如果同一部电影你观看多

【BZOJ 3747】 3747: [POI2015]Kinoman (线段树)

3747: [POI2015]Kinoman Time Limit: 60 Sec  Memory Limit: 128 MBSubmit: 830  Solved: 338 Description 共有m部电影,编号为1~m,第i部电影的好看值为w[i]. 在n天之中(从1~n编号)每天会放映一部电影,第i天放映的是第f[i]部. 你可以选择l,r(1<=l<=r<=n),并观看第l,l+1,-,r天内所有的电影.如果同一部电影你观看多于一次,你会感到无聊,于是无法获得这部电影的好看值

【bzoj3747】Kinoman[POI2015](线段树)

题目传送门:http://www.lydsy.com/JudgeOnline/problem.php?id=3747 对于这种题,考虑固定区间的右端点为r,设区间左端点为l能取得的好看值总和为a[l],那么就相当于当r取不同取值时所有al的最大值. 设last[i]表示第i部电影上一次出现的位置,当右端点r右移1位时,因为只有看了一遍的电影能获取好看值,所以能取得f[r]的好看值的al只能是在last[r]~r这个区间.因此每次右移时,last[last[r]]+1~last[r]减去w[f[r

【BZOJ3747】[POI2015]Kinoman【线段树】

[题目链接] 看到数据范围以为是O(n)做法,再看看status似乎带了个log?然后去搜题解,没想到是线段树= =. 题解: 设pre[i]表示与i这个位置上的数相等的前一个数的下标. 用线段树维护答案. 枚举右端点,每次加入一个数,那么pre[i] + 1到i这段位置的答案加上这个数的权值,pre[pre[i]] + 1到pre[i]这段位置的答案减去这个数的权值,然后查询[1, i]的最大值即可. 复杂度: 时间复杂度O(nlogn),空间复杂度O(n). RE: 给线段树开了1倍n的空间

@bzoj - [email&#160;protected] [POI2015] Kinoman

目录 @[email protected] @[email protected] @accepted [email protected] @[email protected] @[email protected] 共有 m 部电影,第 i 部电影的好看值为 w[i]. 在 n 天之中每天会放映一部电影,第 i 天放映的是第 f[i] 部. 你可以选择 l, r (1 <= l <= r <= n) ,并观看第 l, l+1, -, r 天内所有的电影. 最大化观看且仅观看过一次的电影的好