Codeforces Round #620 Div2F Animal Observation(前缀和+动态规划+线段树维护)

眉间皱痕 提交于 2020-03-06 15:56:26

题意:

作者喜欢观察动物,因此他购买了两个照相机,以拍摄森林中野生动物的视频,一台摄像机的颜色是红色,一台摄像机的颜色是蓝色。

从第1天到第N天,作者将拍摄N天的视频。森林可以分为M个区域,编号从1到M。他将通过以下方式使用相机:

在每个奇数天,将红色相机带到森林中并录制两天的视频。

在每个偶数天,将蓝色相机带到森林中并录制两天的视频。

如果他在第N天使用其中一台摄像机开始录制,则该摄像机仅录制一天。

每个摄像机可以连续观察森林的K个连续的区域。

作者已经获得了有关每天在每个区域看到多少动物的信息。由于他想观察尽可能多的动物,因此他希望你找到放置两台摄像机N天的最佳方法。请注意,如果两个摄像机在同一天观察同一区域,则在该区域观察到的动物仅被计数一次。

输出一个整数,可以观察到的最大动物的数量。

题解:

开一个二维数组记录每一天每个区域的动物数量。

开一个二维数组保存每一天每个区域的前缀和,方便统计。

然后开一个二维dp数组,i表示第i天,j表示从j开始到j+k-1的区域,dp的值表示当前该区域所能拍摄的最大动物数量(前面的日子最大化操作)。

初始化dp数组,第1天dp值为区域当天的动物总和。

然后遍历第2天到第N天,每一天开始,先更新所有的dp值,即把dp值直接加上新一天区域内动物总和。

然后遍历每个区域,把与前一天拍到的重复的区域剪掉,再更新dp的值。

更新dp值这个过程需要维护一颗线段树,线段树里的mx值表示前一天能拍到动物的最大数量,tag表示子节点要加多少,存在父节点里,等查询到子节点了再更新子节点。

#include<bits/stdc++.h>
using namespace std;
const int maxn=20014;
struct node {
    int l;
    int r;
    int mx;
    int tag;
}segTree[maxn*4];
int N,M,K;
int a[60][maxn];
int sum[60][maxn];
int dp[60][maxn];
void build (int i,int l,int r,int x) {
    segTree[i].l=l;
    segTree[i].r=r;
    segTree[i].tag=0;
    if (l==r-1) 
        segTree[i].mx=dp[x-1][l]+sum[x][l+K-1]-sum[x][max(K-1,l-1)];
    else {
        int mid=(l+r)>>1;
        build(i<<1,l,mid,x);
        build(i<<1|1,mid,r,x);
        segTree[i].mx=max(segTree[i<<1].mx,segTree[i<<1|1].mx);
    }
}
void pushdown (int i) {
    segTree[i<<1].mx+=segTree[i].tag;
    segTree[i<<1|1].mx+=segTree[i].tag;
    segTree[i<<1].tag+=segTree[i].tag;
    segTree[i<<1|1].tag+=segTree[i].tag;
    segTree[i].tag=0;
}
void modify (int i,int l,int r,int del) {
    if (l<=segTree[i].l&&r>=segTree[i].r) {
        segTree[i].mx+=del;
        segTree[i].tag+=del;
        return;
    }
    if (segTree[i].tag) pushdown(i);
    int mid=(segTree[i].l+segTree[i].r)>>1;
    if (l<mid) modify(i<<1,l,r,del);
    if (r>mid) modify(i<<1|1,l,r,del);
    segTree[i].mx=max(segTree[i<<1].mx,segTree[i<<1|1].mx);
}
void solve () {
    scanf("%d%d%d",&N,&M,&K);
    for (int i=1;i<=N;i++) {
        sum[i][0]=0;
        for (int j=1;j<=M;j++) {
            scanf("%d",&a[i][j]);
            sum[i][j]=sum[i][j-1]+a[i][j];
        }
    }
    for (int j=1;j<=M-K+1;j++) 
        dp[1][j]=sum[1][j+K-1]-sum[1][j-1];
    for (int i=2;i<=N;i++) {
        build(1,1,M-K+2,i);
        for (int j=1;j<=M-K+1;j++) {
            modify(1,j,j+K,-a[i][j+K-1]);
            dp[i][j]=segTree[1].mx+sum[i][j+K-1]-sum[i][j-1];
            modify(1,max(j-K+1,1),j+1,a[i][j]);
        }
    }
    int ans=dp[N][1];
    for (int j=2;j<=M;j++)
        ans=max(ans,dp[N][j]);
    printf ("%d\n",ans);
}
int main () {
    solve();
    return 0;
}

 

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!