BZOJ 3747 POI2015 Kinoman

因为上午没有准备够题目,结果发现写完这道题没题可写了QAQ

又因为这道题范围是100w,我写了发线段树,以为要T,上午就花了一个小时拼命卡常数

结果下午一交居然过了QAQ

我们考虑枚举L,求最大R使得[L,R]是对于当前L最大权值的区间

考虑每个点的影响

如果从L向右他是第一个,那么他会对后面产生a[f[L]]的贡献

如果从L向右他是第二个,那么他会对后面产生-a[f[L]]的贡献

然后我们维护一棵线段树,每次查询最值并且进行更新即可

include<cstdio>
#include<iostream>
#include<cstdlib>
#include<algorithm>
#include<cstring>
using namespace std;

typedef long long LL;
const int maxn=1000010;
int n,m,x,y,v,tim;
int f[maxn];
int a[maxn];
int next[maxn],h[maxn];
LL mx[maxn<<2],add[maxn<<2];
LL ans=0,tot=0;

inline void read(int &num){
    num=0;char ch=getchar();
    while(ch<‘!‘)ch=getchar();
    while(ch>=‘0‘&&ch<=‘9‘)num=(num<<3)+(num<<1)+ch-‘0‘,ch=getchar();
}
inline LL Max(LL a,LL b){return a>b?a:b;}
inline void push_down(int o){
    int L=(o<<1),R=(L|1);
    add[L]+=add[o];mx[L]+=add[o];
    add[R]+=add[o];mx[R]+=add[o];
    add[o]=0;
}
inline void modify(int o,int L,int R){
    if(L>=x&&R<=y){
        mx[o]+=v;add[o]+=v;
        return;
    }
    if(add[o]!=0)push_down(o);
    int mid=(L+R)>>1;
    if(y<=mid)modify(o<<1,L,mid);
    else if(x>mid)modify(o<<1|1,mid+1,R);
    else modify(o<<1,L,mid),modify(o<<1|1,mid+1,R);
    mx[o]=Max(mx[o<<1],mx[o<<1|1]);
}
inline LL ask(int o,int L,int R){
    if(L>=x&&R<=y)return mx[o];
    if(add[o]!=0)push_down(o);
    int mid=(L+R)>>1;
    if(y<=mid)return ask(o<<1,L,mid);
    else if(x>mid)return ask(o<<1|1,mid+1,R);
    else return Max(ask(o<<1,L,mid),ask(o<<1|1,mid+1,R));
}
int main(){
    read(n);read(m);y=n;
    for(int i=1;i<=n;++i)read(f[i]);
    for(int i=1;i<=m;++i)read(a[i]);
    for(int i=n;i>=1;--i){
        next[i]=h[f[i]];
        h[f[i]]=i;
    }
    for(int i=1;i<=m;++i){
        if(h[i]){
            x=h[i];v=a[i];
            modify(1,1,n);
            if(next[h[i]]){
                x=next[h[i]];v=-a[i];
                modify(1,1,n);
            }
        }
    }
    for(int L=1;L<=n;++L){
        x=L;
        ans=Max(ans,ask(1,1,n)-tot);
        tot+=a[f[L]];
        int n1=next[L],n2=next[n1];
        if(n1){
            x=n1;v=(a[f[L]]<<1);
            modify(1,1,n);
        }
        if(n2){
            x=n2;v=-a[f[L]];
            modify(1,1,n);
        }
    }printf("%lld\n",ans);
    return 0;
}

  

时间: 2024-10-09 15:42:43

BZOJ 3747 POI2015 Kinoman的相关文章

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

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

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

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

[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>

@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 天内所有的电影. 最大化观看且仅观看过一次的电影的好

【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&

BZOJ 3747 POI 2015 Kinoman 线段树

题目大意:给出电影院的放映电影顺序,一个电影只有看过一次的时候会获得电影的权值.没看过或者看两次或以上都不能获得权值.问看连续区间的电影能够获得的最大权值是多少. 思路:利用线段树维护前缀和.将出现第一次的地方的权值加上那部电影的权值,第二次出现的时候权值减去那部电影的权值.枚举起点,先更新答案,然后在当前节点减去权值的二倍,然后再在下一次出现的地方加上权值(我感觉我没说明白,总之看代码吧... CODE: #include <cstdio> #include <cstring>

BZOJ 3747 Kinoman

和HEOI采花类似. #include<iostream> #include<cstdio> #include<cstring> #include<algorithm> #define maxn 1000050 using namespace std; int n,m,f[maxn],w[maxn],regis[maxn],nxt[maxn]; int root,tot=0,ls[maxn<<2],rs[maxn<<2],cnt[ma