[POI2015]KIN (线段树)

题目链接


Solution

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

Code

#include<bits/stdc++.h>
#define N 1000008
#define ll long long
#define mid (l+r>>1)
#define in(x) x=read()
using namespace std;

int read()
{
    char ch=getchar(); int w=0;
    while(ch<'0'||ch>'9')ch=getchar();
    while(ch>='0'&&ch<='9'){w=w*10+ch-'0';ch=getchar();}
    return w;
}
ll pre[N],sgx[N*4],n,k,lazy[N*4];
ll col[N],w[N],lst[N],ans;

void push_down(int node)
{
    if(!lazy[node])return;
    lazy[node<<1]+=lazy[node]; lazy[node<<1|1]+=lazy[node];
    sgx[node<<1|1]+=lazy[node]; sgx[node<<1]+=lazy[node];
    lazy[node]=0;
}

void change(int node,int l,int r,int L,int R,int v)
{
    if(l>R||r<L)return;
    if(l>=L&&r<=R){lazy[node]+=v;sgx[node]+=v;return;}
    push_down(node);
    change(node<<1,l,mid,L,R,v);
    change(node<<1|1,mid+1,r,L,R,v);
    sgx[node]=max(sgx[node<<1],sgx[node<<1|1]);
    return;
}

int main()
{
    in(n),in(k);
    for(int i=1;i<=n;i++){
        in(col[i]);
        pre[i]=lst[col[i]];
        lst[col[i]]=i;
    }
    for(int i=1;i<=k;i++)in(w[i]);
    for(int i=1;i<=n;i++){
        change(1,1,n,pre[i]+1,i,w[col[i]]);
        change(1,1,n,pre[pre[i]]+1,pre[i],-w[col[i]]);
        ans=max(ans,sgx[1]);
    }
    cout<<ans<<endl;
}

原文地址:https://www.cnblogs.com/Kv-Stalin/p/9743307.html

时间: 2024-11-08 23:01:04

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

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.

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