P3582 [POI2015]KIN 线段树

  

题目描述

共有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],…,fn。第三行包含m个整数w[1],w[2],…,wm

输出格式

输出观看且仅观看过一次的电影的好看值的总和的最大值。

输入输出样例

输入 #1复制

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

输出 #1复制

15

说明/提示

共有m部电影,编号为1~m,第i部电影的好看值为w[i]。

在n天之中(从1~n编号)每天会放映一部电影,第i天放映的是第f[i]部。

你可以选择l,r(1<=l<=r<=n),并观看第l,l+1,…,r天内所有的电影。如果同一部电影你观看多于一次,你会感到无聊,于是无法获得这部电影的好看值。所以你希望最大化观看且仅观看过一次的电影的好看值的总和。

有两种写法:

https://www.luogu.org/problemnew/solution/P3582

求最大连续子段   保证相同的场次最多只有2个  前一个为-w  后一个为w

#include<bits/stdc++.h>
using namespace std;
#define rep(i,a,b) for(int i=(a);i<=(b);i++)
#define repp(i,a,b) for(int i=(a);i>=(b);--i)
#define ll long long
#define see(x) (cerr<<(#x)<<‘=‘<<(x)<<endl)
#define inf 0x3f3f3f3f
#define CLR(A,v)  memset(A,v,sizeof A)
//////////////////////////////////
const int N=2e6+10;

ll t[N<<2],lmax[N<<2],rmax[N<<2],sum[N<<2];

void up(int pos)
{
    sum[pos]=sum[pos<<1]+sum[pos<<1|1];
    lmax[pos]=max(lmax[pos<<1],sum[pos<<1]+lmax[pos<<1|1]);
    rmax[pos]=max(rmax[pos<<1|1],sum[pos<<1|1]+rmax[pos<<1]);
    t[pos]=max(max(t[pos<<1],t[pos<<1|1]),rmax[pos<<1]+lmax[pos<<1|1]);
}
void upnode(int x,int v,int l,int r,int pos)
{
    if(l==r){sum[pos]=lmax[pos]=rmax[pos]=t[pos]=1ll*v;return;}
    int m=(l+r)>>1;
    if(x<=m)upnode(x,v,l,m,pos<<1);
    else upnode(x,v,m+1,r,pos<<1|1);
    up(pos);
}
int n,m,w[N],f[N],fi[N],se[N];

int main()
{
    scanf("%d%d",&n,&m);
    rep(i,1,n)scanf("%d",&f[i]);
    rep(i,1,m)scanf("%d",&w[i]);
    ll ans=0;
    rep(i,1,n)
    {
        if(!fi[f[i]])fi[f[i]]=i,upnode(i,w[f[i]],1,n,1);
        else if(!se[f[i]])se[f[i]]=i,upnode(fi[f[i]],-w[f[i]],1,n,1),upnode(i,w[f[i]],1,n,1);
        else upnode(fi[f[i]],0,1,n,1),upnode(se[f[i]],-w[f[i]],1,n,1),upnode(i,w[f[i]],1,n,1),fi[f[i]]=se[f[i]],se[f[i]]=i;
        ans=max(ans,t[1]);
    }
    cout<<ans;
    return 0;
}

原文地址:https://www.cnblogs.com/bxd123/p/11419207.html

时间: 2024-10-17 08:02:40

P3582 [POI2015]KIN 线段树的相关文章

[POI2015]KIN (线段树)

题目链接 Solution 线段树. 观察题目可以得到一个小 \(trick\) : 对于任意一个节点 \(i\) ,那么和它颜色相同的上一个节点 \(pre[i]\),肯定不会放在一个区间. 于是考虑对于每一个节点计算它可以献出贡献的区间. 先 \(O(n)\) 扫出每一个点的 \(pre\) . 然后从左往右,一次将节点可以贡献的范围即 \([pre_i~,~nxt_i)\). 同时将同颜色的上一个节点贡献删除. 然后线段树统计全局最大值即可. Code #include<bits/stdc

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

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

【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

3747: [POI2015]Kinoman|线段树

枚举左区间线段树维护最大值 #include<algorithm> #include<iostream> #include<cstdlib> #include<cstring> #include<cstdio> #include<vector> #include<cmath> #include<queue> #include<set> #include<map> #define ll l

【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

【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天内所有的电影.如果同一部电影你观看多于一次,你会感到无聊,于是无法获得这部电影的好看值

P3588 [POI2015]PUS(拓扑排序+线段树)

P3588 [POI2015]PUS 对于每个$(l,r,k)$,将$k$个位置向剩下$r-l-k+1$个位置连边,边权为$1$,这样就保证$k$个位置比剩下的大 先给所有位置填$1e9$保证最优 然后拓扑排序填数 填的数不在$[1,1e9]$内或者出现环,即为不合法 但是这样边数过多会超时 于是考虑线段树优化建图 把$n$个点建成线段树,每个节点向左右儿子连边,边权为0. 这样每次连一个区间$[l,r]$就只需要$log(r-l+1)$次 注意不合法情况要枚举完整 #include<iostr

【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的空间