Codeforces Round #620 (Div. 2)F2

题意:给出n,和m表示有n天,m块区域,每块区域都有一定数论的动物数量,k表示可以在这一天中观察[x,max(x+k-1,m)]的区域内的动物,有俩台相机,一台只能在偶数天用,另一台则是在奇数天用,每用一次就得在那个区域内待俩天,相邻的要是有重复的区域,该区域内的动物数只计数一次,问最多有可能的动物数目是多少

分析:因为n<=50,m<=20000,所以我们考虑一下dp[n][m],dp[i][j]表示:在第 i 天选择[j,j+k-1]区域拍摄的最大拍摄量。

   因为是连续拍摄俩天,所以我们可以想象一个块,这个块的大小是:2*k(2为连续拍摄俩天,k为连续的区域),然后第 i 天转移的过程就是这个块滑块的过程,下面考虑第 i 天;

   因为会涉及重复的问题,所以我们不妨直接把这个块全部算成都有效的块,然后这个块要和 (前一天的dp (减去这个块对这个前一天的dp)的影响) 相加才为选择这个区域来拍摄的最优值;

   接下来的dp就相当于这个块在第 i 天的dp数组上进行“滑块”,假设当前这个块(左上角为dp[i][j],dp[i+1][j+k-1])向右 “滑” ,那么就考虑加入[j+k]区域的动物对dp[i-1][]的影响和去掉 [j] 区域对dp[i-1][]的影响;

   这个影响靠线段树的区间加,区间最值来维护,详细可以看代码注释

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
#define pb push_back
#define lson root<<1,l,midd
#define rson root<<1|1,midd+1,r
const int M=2e4+4;
const int N=55;
int sum[N][M],a[N][M],dp[N][M];
struct node{
    int lazy,val;
}tr[M<<2];
void build(int root,int l,int r){
    if(l==r){
        tr[root].lazy=tr[root].val=0;
        return ;
    }
    int midd=(l+r)>>1;
    build(lson);
    build(rson);
}
void pushdown(int root){
    int x=tr[root].lazy;
    tr[root<<1].lazy+=x;
    tr[root<<1|1].lazy+=x;
    tr[root<<1].val+=x;
    tr[root<<1|1].val+=x;
    tr[root].lazy=0;
}
void up(int root){
    tr[root].val=max(tr[root<<1].val,tr[root<<1|1].val);
}
void update(int L,int R,int c,int root,int l,int r){
    if(L<=l&&r<=R){
        tr[root].lazy+=c;
        tr[root].val+=c;
        return ;
    }
    int midd=(l+r)>>1;
    if(tr[root].lazy)
        pushdown(root);
    if(L<=midd)
        update(L,R,c,lson);
    if(R>midd)
        update(L,R,c,rson);
    up(root);
}
int query(int L,int R,int root,int l,int r){
    if(L<=l&&r<=R){
        return tr[root].val;
    }
    if(tr[root].lazy)
        pushdown(root);
    int res=0;
    int midd=(l+r)>>1;
    if(L<=midd)
        res=max(res,query(L,R,lson));
    if(R>midd)
        res=max(res,query(L,R,rson));
    up(root);
    return res;
}
int main(){
    ios::sync_with_stdio(false);
    cin.tie(0);
    int n,m,k;
    cin>>n>>m>>k;
    for(int i=1;i<=n;i++){
        for(int j=1;j<=m;j++){
            cin>>a[i][j];
            sum[i][j]=sum[i][j-1]+a[i][j];
        }
    }
    ///预处理dp[1]的情况
    for(int j=1;j<=m-k+1;j++){
        dp[1][j]=sum[1][j+k-1]-sum[1][j-1]
                +sum[2][j+k-1]-sum[2][j-1];
    }
    for(int i=2;i<=n;i++){
        memset(tr,0,sizeof(tr));
        for(int j=1;j<=m;j++)
            update(j,j,dp[i-1][j],1,1,m);
        for(int j=1;j<=k;j++)///算一个小预处理 ,为第一个窗口计算做准备
            update(1,j,-a[i][j],1,1,m);
            ///枚举每一个窗口
        for(int j=1;j<=m-k+1;j++){///每个窗口为上一个窗口向右移动一格,代价为去掉左边一个增加右边一个对答案的贡献
            dp[i][j]=max(dp[i][j],query(1,m,1,1,m)+sum[i][j+k-1]-sum[i][j-1]
                                              +sum[i+1][j+k-1]-sum[i+1][j-1]);
            update(max(1,j-k+1),j,a[i][j],1,1,m);///减去左边出队的
            update(j+1,j+k,-a[i][j+k],1,1,m);///加上右边入队的
        }
    }
    int ans=0;
    for(int j=1;j<=m;j++)
        ans=max(ans,dp[n][j]);
    cout<<ans<<endl;
    return 0;
}

原文地址:https://www.cnblogs.com/starve/p/12336946.html

时间: 2024-08-05 14:30:59

Codeforces Round #620 (Div. 2)F2的相关文章

Codeforces Round #620(Div. 2)

Codeforces Round #620 (Div. 2) 题目链接 https://codeforces.com/contest/1313 A B C题直接跳过. D题: 求最短的上升子序列,我们可以直接假设它是1-n从大到小排列,然后对于每两个大于号之间的小于号再翻转就行. 求最长的就反过来做一遍就行. 代码: #include<bits/stdc++.h> using namespace std; int n,a[300050],ans[300050]; char s[300050];

Codeforces Round #620 (Div. 2)D dilworld定理

题:https://codeforces.com/contest/1304/problem/D 题意:给定长度为n-1的只含’>'和‘<’的字符串,让你构造出俩个排列,俩个排列相邻的数字之间要满足这个字符串,找出的俩个要是最小化最长上升子序列,和最大化最长上升子序列: 分析:dilworld定理,最长下降子序列个数等于最长上升子序列的长度 先说构造最小的,LIS的长度就等于最长的连续'<'的长度加1,贪心地把最大的数放在最前面: 构造最大的就把最小的数尽可能放在前面 原文地址:https

Codeforces Round #257 (Div. 2) A/B/C/D

前三题早就写好了,一直在纠结D A. Jzzhu and Children 题意:就是简单的模拟,给排成一队的孩子分发糖果,每个孩子有至少要得到的糖果数. 然后每次给队头的孩子分发m个糖果,如果他已经得到了足够的糖果(大于等于他想得到的 最少糖果数)那么他就出队,否则他就去队尾.问最后一个孩子的编号. 算法:队列模拟,水题~ #include<cstdio> #include<iostream> #include<cstring> #include<queue&g

Codeforces Round #FF (Div. 1)-A,B,C

A:DZY Loves Sequences 一开始看错题了..sad. 题目很简单,做法也很简单.DP一下就好了. dp[i][0]:到当前位置,没有任何数改变,得到的长度. dp[i][1]:到当前位置,改变了一个数,得到的长度 不过需要正向求一遍,然后反向求一遍. #include<iostream> #include<stdio.h> #include<algorithm> #include<stdlib.h> #include<string.h

Codeforces Round #245 (Div. 2)

A Points and Segments (easy) 智商题,(智商捉急~) /*********************************************************** *分析:只要按Xi从小到大染成1010101010... , *1.0间隔的的序列就能保证对于任意区间[l, r]中1的个数和0的个数之差小于等于1. *注意:由于输入的Xi可能是无序的,所有要两次排序处理. *******************************************

【codeforces】Codeforces Round #277 (Div. 2) 解读

门户:Codeforces Round #277 (Div. 2) 486A. Calculating Function 裸公式= = #include <cstdio> #include <cstring> #include <algorithm> using namespace std ; typedef long long LL ; LL n ; int main () { while ( ~scanf ( "%I64d" , &n )

Codeforces Round #257 (Div. 2/B)/Codeforces450B_Jzzhu and Sequences

B解题报告 算是规律题吧,,,x y z -x -y -z 注意的是如果数是小于0,要先对负数求模再加模再求模,不能直接加mod,可能还是负数 给我的戳代码跪了,,, #include <iostream> #include <cstring> #include <cstdio> using namespace std; long long x,y,z; long long n; int main() { cin>>x>>y; cin>&g

Codeforces Round #632 (Div. 2) 部分题解

目录 Codeforces Round #632 (Div. 2) A. Little Artem B. Kind Anton C. Eugene and an array D. Challenges in school №41 F. Kate and imperfection Codeforces Round #632 (Div. 2) A. Little Artem 题意:略. 分析:构造这样的图形: BWW...W BWW...W BBB...B #include <bits/stdc++

Codeforces Round #428 (Div. 2)

Codeforces Round #428 (Div. 2) A    看懂题目意思就知道做了 #include<bits/stdc++.h> using namespace std; #pragma comment(linker, "/STACK:102400000,102400000") #define rep(i,a,b) for (int i=a; i<=b; ++i) #define per(i,b,a) for (int i=b; i>=a; --i