zoukankan      html  css  js  c++  java
  • 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
    ",ans);
    	return 0;
    }
    
    ——by zhber,转载请注明来源
  • 相关阅读:
    Django学习笔记
    禁用Win10自带截图工具快捷键(Shift+Win+S)
    Linux基础知识
    Ubuntu中配置Python虚拟环境Virtualenv
    PyCharm 格式化代码 常用快捷键
    你不得不知道的HashMap面试连环炮
    大型互联网公司分布式ID方案总结
    Java程序员必会常用Linux速查手册
    面试题:InnoDB中一棵B+树能存多少行数据?
    C语言:标准IO_fopen( )、fclose() ①
  • 原文地址:https://www.cnblogs.com/zhber/p/4175810.html
Copyright © 2011-2022 走看看