【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]],last[r]+1~r加上w[f[r]]。

  具体实现, 就是维护一个资瓷区间加与查询区间最大的线段树。

  代码(常数真大):

#include<cstdio>
#include<cstring>
#include<cmath>
#include<cstdlib>
#include<ctime>
#include<algorithm>
#include<vector>
#include<queue>
#include<map>
#define ll long long
#define max(a,b) (a>b?a:b)
#define min(a,b) (a<b?a:b)
#define inf 1ll<<60
ll read()
{
    ll tmp=0; char c=getchar(),f=1;
    for(;c<‘0‘||‘9‘<c;c=getchar())if(c==‘-‘)f=-1;
    for(;‘0‘<=c&&c<=‘9‘;c=getchar())tmp=tmp*10+c-‘0‘;
    return tmp*f;
}
using namespace std;
struct point{
    ll delta,mx;
}a[4000010];
ll w[1000010];
int f[1000010],last[1000010],pos[1000010];
int n,m;
void add(int now,int l,int r,int x,int y,ll k)
{
    if(x<=l&&r<=y){
        a[now].delta+=k; a[now].mx+=k;
    }
    else{
        int mid=(l+r)>>1;
        if(x<=mid)add(now<<1,l,mid,x,y,k);
        if(mid<y)add(now<<1|1,mid+1,r,x,y,k);
        a[now].mx=max(a[now<<1].mx,a[now<<1|1].mx)+a[now].delta;
    }
}
ll getmax(int now,int l,int r,int x,int y)
{
    if(x<=l&&r<=y)return a[now].mx;
    else{
        ll tmp=-inf; int mid=(l+r)>>1;
        if(x<=mid)tmp=max(tmp,getmax(now<<1,l,mid,x,y));
        if(mid<y)tmp=max(tmp,getmax(now<<1|1,mid+1,r,x,y));
        return tmp+a[now].delta;
    }
}
int main()
{
    int i;
    n=read(); m=read();
    last[0]=-1;
    for(i=1;i<=n;i++){
        f[i]=read();
        if(!pos[f[i]])last[i]=0;
        else last[i]=pos[f[i]];
        pos[f[i]]=i;
    }
    for(i=1;i<=m;i++)w[i]=read();
    ll ans=-inf;
    for(i=1;i<=n;i++){
        add(1,1,n,last[i]+1,i,w[f[i]]);
        if(~last[last[i]])add(1,1,n,last[last[i]]+1,last[i],-w[f[i]]);
        ans=max(ans,getmax(1,1,n,1,i));
    }
    printf("%lld\n",ans);
}

bzoj3747

原文地址:https://www.cnblogs.com/quzhizhou/p/8308879.html

时间: 2024-11-08 20:53:18

【bzoj3747】Kinoman[POI2015](线段树)的相关文章

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

BZOJ 3747: [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]

[BZOJ 3747] [POI 2015] Kinoman【线段树】

Problem Link : BZOJ 3747 题解:ZYF-ZYF 神犇的题解 解题的大致思路是,当区间的右端点向右移动一格时,只有两个区间的左端点对应的答案发生了变化. 从 f[i] + 1 到 i 的区间中的答案增加了 W[A[i]], 从 f[f[i]] + 1 到 f[i] 的区间的答案减少了 W[A[i]] ,其余区间的答案没有发生变化. 那么就是线段树的区间修改和区间最值查询. 代码如下: #include <iostream> #include <cstdio>

【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

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

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