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

线段树...

我们可以枚举左端点 , 然后用线段树找出所有右端点中的最大值 .

-----------------------------------------------------------------------------------------

#include<cstdio>

#include<algorithm>

#include<cstring>

#include<iostream>

#define rep( i , n ) for( int i = 0 ; i < n ; ++i )

#define clr( x , c ) memset( x , c , sizeof( x ) )

#define Rep( i , n ) for( int i = 1 ; i <= n ; ++i )

#define M( l , r ) ( ( l + r ) >> 1 )

using namespace std;

typedef long long ll;

const int maxn = 1000000 + 5;

int L , R , v , n;

struct Node {

Node *lc , *rc;

ll mx , v , add;

Node() {

mx = v = add = 0;

lc = rc = NULL;

}

inline void Add( ll ad ) {

add += ad;

}

inline void update() {

if( lc )

mx = max( lc -> mx , rc -> mx ) + add;

else

mx =  (v += add ) , add = 0;

}

inline void pushdown() {

lc -> Add( add );

rc -> Add( add );

add = 0;

}

} nodePool[ maxn << 1 ] , *pt = nodePool , *root;

void build( Node* t , int l , int r ) {

if( r > l ) {

build( t -> lc = pt++ , l , M( l , r ) );

build( t -> rc = pt++ , M( l , r ) + 1 , r );

}

t -> update();

}

// add

void modify( Node* t , int l , int r ) {

if( L <= l && r <= R )

t -> Add( v );

else {

t -> pushdown();

int m = M( l , r );

L <= m ? modify( t -> lc , l , m ) : t -> lc -> update();

m < R ? modify( t -> rc , m + 1 , r ) : t -> rc -> update();

}

t -> update();

}

int w[ maxn ];

struct day {

int f;

day* next;

day() {

next = NULL;

}

} *head[ maxn ] , *last[ maxn ] , d[ maxn ];

int main() {

freopen( "test.in" , "r" , stdin );

clr( last , 0 );

int m;

cin >> n >> m;

build( root = pt++ , 1 , n );

Rep( i , n ) {

head[ i ] = d + i;

scanf( "%d" , &head[ i ] -> f );

}

Rep( i , m )

scanf( "%d" , w + i );

for( int i = n ; i ; i-- ) {

head[ i ] -> next = last[ head[ i ] -> f ];

last[ head[ i ] -> f ] = head[ i ];

}

Rep( i , m ) if( last[ i ] ) {

L = last[ i ] - d , R = last[ i ] -> next ? last[ i ] -> next - d - 1 : n;

v = w[ i ];

modify( root , 1 , n );

}

ll ans = 0;

Rep( i , n ) {

ans = max( ans , root -> mx );

if( head[ i ] -> next ) {

L = i , R = head[ i ] -> next - d - 1;

v = -w[ head[ i ] -> f ];

modify( root , 1 , n );

L = head[ i ] -> next - d , v = w[ head[ i ] -> f ];

if( head[ i ] -> next -> next )

R = head[ i ] -> next -> next - d - 1;

else

R = n;

modify( root , 1 , n );

} else {

L = i , R = n , v = -w[ head[ i ] -> f ];

modify( root , 1 , n );

}

}

cout << ans << "\n";

return 0;

}

-----------------------------------------------------------------------------------------

3747: [POI2015]Kinoman

Time Limit: 60 Sec  Memory Limit: 128 MB
Submit: 440  Solved: 165
[Submit][Status][Discuss]

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。

HINT

Source

鸣谢Jcvb

时间: 2024-10-13 09:59:14

BZOJ 3747: [POI2015]Kinoman( 线段树 )的相关文章

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

【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 POI2015 Kinoman

因为上午没有准备够题目,结果发现写完这道题没题可写了QAQ 又因为这道题范围是100w,我写了发线段树,以为要T,上午就花了一个小时拼命卡常数 结果下午一交居然过了QAQ 我们考虑枚举L,求最大R使得[L,R]是对于当前L最大权值的区间 考虑每个点的影响 如果从L向右他是第一个,那么他会对后面产生a[f[L]]的贡献 如果从L向右他是第二个,那么他会对后面产生-a[f[L]]的贡献 然后我们维护一棵线段树,每次查询最值并且进行更新即可 include<cstdio> #include<i

【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】Kinoman 线段树

3747: [POI2015]Kinoman Time Limit: 60 Sec  Memory Limit: 128 MBSubmit: 715  Solved: 294[Submit][Status][Discuss] Description 共有m部电影,编号为1~m,第i部电影的好看值为w[i]. 在n天之中(从1~n编号)每天会放映一部电影,第i天放映的是第f[i]部. 你可以选择l,r(1<=l<=r<=n),并观看第l,l+1,…,r天内所有的电影.如果同一部电影你观看多

bzoj 2120: 数颜色 线段树套平衡树

/************************************************************** Problem: 2120 User: wangyucheng Language: C++ Result: Time_Limit_Exceed ****************************************************************/ #include<iostream> #include<cstdio> #incl

Bzoj 2752 高速公路 (期望,线段树)

Bzoj 2752 高速公路 (期望,线段树) 题目链接 这道题显然求边,因为题目是一条链,所以直接采用把边编上号.看成序列即可 \(1\)与\(2\)号点的边连得是. 编号为\(1\)的点.查询的时候把\(r - 1\)就好了. 这里的期望显然就是路径的平均值. 期望值: \[\dfrac{\sum_{i=l}^r\sum_{j=l}^{r}dis[i][j]}{C_{r-l+1}^2}\] 下面部分可以直接算出: 上面这一部分比较难维护. 考虑每一条边会被走过多少次. \[ans = \su

BZOJ 3747 POI 2015 Kinoman 线段树

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