1304F2 - Animal Observation (hard version) 线段树or单调队列 +DP

题意

用摄像机观察动物,有两个摄像机,一个可以放在奇数天,一个可以放在偶数天。摄像机在同一天可以同时照到k个区域放下去可以持续两天。现在给出每一天每个区域的动物数量,问最多照到动物多少个。如果两个照相机同时照到一个动物只算一次。n<=50 k<=m<=2e4

思路

我们可以考虑只在一天的情况 那么就是一个简单的dp,状态为dp[i][j]第i天放置在区域j可以获得的最大数。那么dp[i][j]转移的时候只要取max(dp[i-1])再加上第i天放在j的收益即可。持续天数变成了两天有什么差异呢?那就是会产生覆盖的情况,如何解决这个情况呢,对于dp[i][j]来说 只有上一天的摄像机区间会和[j,j+k-1]相交的时候才要考虑这种情况,那么相交的时候,相当于上一层状态要减去和[j,j+k-1]的交集区间 例如对于dp[i-1][j+1]来说,就是要减去[j,j+k-1]和[j+1,j+k]的交集动物数也就是[j+1,j+k-1]的动物数,这里可以用滑动窗口来维护。对于长度为k的滑动窗口,当前窗口边界为j,如果j出去,表示所有[max(1,j-k+1),j]都应该加上a[i][j]那么新进来的j+k则表示所有[j+1,j+k]都应该减去a[i][j+k]因为这里采用的是区间加减的形式,所以可以用线段树进行维护,同时也可以用单调队列做法。

单调栈做法

对于dp[i-1][x]单调栈是把答案分成了三种情况讨论

1.x位于位于j-k+1的左边 即dp[i][j]由没有相交的情况转移过来直接求前缀最大值即可

2. x位于j+k-1的右边 同上 求后缀最大

3. 和[j,j+k-1]有相交的区间 这个时候就要用单调栈维护最大值了,我们可以知道的是我们维护一个k大的窗口,那么这个k大窗口里的东西都和[j,j+k-1]有相交,那么我们可以把每个入队的dp[i-1][j]减去sum[i][j..j+k-1]压入单调栈,那么对于我们求dp[i][j]而言,值qv[tail]+sum[i][q[tail]...j-1]则为减去了相交区间sum的相应dp值因为qv[tail]+sum[q[tail]..j-1]是单调递增的,即j移动的时候sum[q[tail]..j-1]要增加,就相当于对单调栈里面的每一个值都加上了了一个相同的数,单调栈里的元素的rank不会发生改变,当要状态转移的时候只需要还原值即可

这里有一个技巧对于[x1,y1] [x2,y2]如果交集是[y1,x2]即交叉的情况,如果我们要计算[x2,y2]减去[x2,y1]的值,那么我们对于区间[x1,y1]可以直接减去区间[1...y1]的值,到[x1,y1]的时候 直接加上区间[1,x2-1]则只剩下了[x2,y1]的值了,这样对于队列里的每个值都是加上区间[1,x2-1] 滑动窗口滑动的时候仅仅是x2变化即统一加的值变化了

线段树做法代码

#include<bits/stdc++.h>
#define pb push_back
#define mkp make_pair
using namespace std;
typedef long long ll;
const int maxn=5e5+5;
const int mod=1e9+7;
int dp[55][maxn],a[55][maxn],sum[55][maxn];
int lazy[maxn<<2],tree[maxn<<2];
void build(int o,int l,int r){
lazy[o]=tree[o]=0;
if(l==r)return ;
int mid=l+r>>1;
build(o<<1,l,mid);
build(o<<1|1,mid+1,r);
}
void push_down(int o){
if(lazy[o]){
lazy[o<<1|1]+=lazy[o];
lazy[o<<1]+=lazy[o];
tree[o<<1|1]+=lazy[o];
tree[o<<1]+=lazy[o];
lazy[o]=0;
}
}
void push_up(int o){
tree[o]=max(tree[o<<1],tree[o<<1|1]);
}
void update(int o,int l,int r,int x,int y,int value){
if(l>=x&&r<=y){
tree[o]+=value;
lazy[o]+=value;
return ;
}
int mid=l+r>>1;
push_down(o);
if(mid>=x)update(o<<1,l,mid,x,y,value);
if(mid<y)update(o<<1|1,mid+1,r,x,y,value);
push_up(o);
}
int query(int o,int l,int r,int x,int y){
if(x<=l&&r<=y){
push_down(o);
return tree[o];
}
int mid=l+r>>1;
int ans=0;
if(mid>=x)ans=max(ans,query(o<<1,l,mid,x,y));
if(mid<y)ans=max(ans,query(o<<1|1,mid+1,r,x,y));
return ans;
}
int main(){
int n,m,k;
scanf("%d%d%d",&n,&m,&k);
for(int i=1;i<=n;i++){
for(int j=1;j<=m;j++){
scanf("%d",&a[i][j]);
sum[i][j]=sum[i][j-1]+a[i][j];
}
}
int up=m-k+1;
for(int i=1;i<=up;i++){
dp[1][i]=sum[1][i+k-1]-sum[1][i-1]+sum[2][i+k-1]-sum[2][i-1];
}
for(int i=2;i<=n;i++){
build(1,1,up);
for(int j=1;j<=up;j++)update(1,1,up,j,j,dp[i-1][j]);
for(int j=1;j<=k;j++)update(1,1,up,1,j,-a[i][j]);
for(int j=1;j<=up;j++){
dp[i][j]=query(1,1,up,1,up)+sum[i][j+k-1]-sum[i][j-1]+sum[i+1][j+k-1]-sum[i+1][j-1];
if(j<up){
update(1,1,up,max(1,j-k+1),j,a[i][j]);
update(1,1,up,j+1,j+k,-a[i][j+k]);
}
}
}
printf("%d\n",*max_element(dp[n]+1,dp[n]+up+1));
return 0;
}

单调栈

#include<bits/stdc++.h>
#define pb push_back
#define mkp make_pair
using namespace std;
typedef long long ll;
const int maxn=5e5+5;
const int mod=1e9+7;
int dp[55][maxn],a[55][maxn],sum[55][maxn],q[maxn],qv[maxn];
int n,m,k;
int query(int i,int l){
return sum[i][l+k-1]-sum[i][l-1];
}
int main(){
scanf("%d%d%d",&n,&m,&k);
for(int i=1;i<=n;i++){
for(int j=1;j<=m;j++){
scanf("%d",&a[i][j]);
sum[i][j]=sum[i][j-1]+a[i][j];
}
}
int up=m-k+1;
for(int i=1;i<=up;i++){
dp[1][i]=query(1,i)+query(2,i);
}
for(int i=2;i<=n;i++){
int maxnum=0;
for(int j=1;j<=up;j++){
if(j-k>=1)maxnum=max(maxnum,dp[i-1][j-k]);
dp[i][j]=max(dp[i][j],maxnum+query(i,j)+query(i+1,j));
}
maxnum=0;
for(int j=up;j>=1;j--){
if(j+k<=up)maxnum=max(maxnum,dp[i-1][j+k]);
dp[i][j]=max(dp[i][j],maxnum+query(i,j)+query(i+1,j));
}
int tail=0,head=1;
for(int j=1;j<=up;j++){
int v=dp[i-1][j]-sum[i][j+k-1];
while(head<=tail&&v>=qv[tail])tail--;
qv[++tail]=v;q[tail]=j;
dp[i][j]=max(dp[i][j],qv[head]+sum[i][j-1]+query(i,j)+query(i+1,j));
while(head<=tail&&q[head]<=j-k+1)head++;
}
tail=0,head=1;
for(int j=up;j>=1;j--){
int v=dp[i-1][j]+sum[i][j-1];
while(head<=tail&&v>=qv[tail])tail--;
qv[++tail]=v;q[tail]=j;
dp[i][j]=max(dp[i][j],qv[head]-sum[i][j+k-1]+query(i,j)+query(i+1,j));
while(head<=tail&&q[head]>=j+k-1)head++;
}
}
printf("%d\n",*max_element(dp[n]+1,dp[n]+1+up));
return 0;
}

1304F2 - Animal Observation (hard version) 线段树or单调队列 +DP的更多相关文章

  1. bzoj 1171 并查集优化顺序枚举 | 线段树套单调队列

    详见vfleaking在discuss里的题解. 收获: 当我们要顺序枚举一个序列,并且跳过某些元素,那么我们可以用并查集将要跳过的元素合并到一起,这样当一长串元素需要跳过时,可以O(1)跳过. 暴力 ...

  2. PKU 2823 Sliding Window(线段树||RMQ||单调队列)

    题目大意:原题链接(定长区间求最值) 给定长为n的数组,求出每k个数之间的最小/大值. 解法一:线段树 segtree节点存储区间的最小/大值 Query_min(int p,int l,int r, ...

  3. ACM学习历程—HDU 5289 Assignment(线段树 || RMQ || 单调队列)

    Problem Description Tom owns a company and he is the boss. There are n staffs which are numbered fro ...

  4. POJ 2823 线段树 Or 单调队列

    时限12s! 所以我用了线段树的黑暗做法,其实正解是用单调队列来做的. //By SiriusRen #include <cstdio> #include <cstring> ...

  5. 【HDU6701】Make Rounddog Happy【权值线段树+双向单调队列】

    题意:给你一个序列,求满足要求的子序列个数,其中要求为: 1.子序列的max-子序列长度len<=k 2.子序列中不出现重复的数字 题解:首先看到子序列max,很容易想到枚举最大值然后分治,这个 ...

  6. Luogu4085 [USACO17DEC]Haybale Feast (线段树,单调队列)

    \(10^18\)是要long long的. \(nlogn\)单调队列上维护\(logn\)线段树. #include <iostream> #include <cstdio> ...

  7. Codeforces 1304F2 Animal Observation (hard version) 代码(dp滑动窗口线段树区间更新优化)

    https://codeforces.com/contest/1304/problem/F2 #include<bits/stdc++.h> using namespace std; ; ...

  8. Codeforces Round #620 F2. Animal Observation (hard version) (dp + 线段树)

    Codeforces Round #620 F2. Animal Observation (hard version) (dp + 线段树) 题目链接 题意 给定一个nm的矩阵,每行取2k的矩阵,求总 ...

  9. 【Codeforces720D】Slalom 线段树 + 扫描线 (优化DP)

    D. Slalom time limit per test:2 seconds memory limit per test:256 megabytes input:standard input out ...

随机推荐

  1. 解读前端js中签名算法伪造H5游戏加分

    信息安全在我们日常开发中息息相关,稍有忽视则容易产生安全事故.对安全测试也提出更高要求.以下是笔者亲自实践过程: 一. 打开某个数钱游戏HTML5页面,在浏览器 F12 开发工具中,查看的js,如下, ...

  2. python+selenium自动化测试,浏览器最大化报错解决方法

    此处以谷歌浏览器为例 [问题1]缺少chrome驱动,webdriver调用谷歌浏览器的时候就报错了,如下图: [原因分析]缺少谷歌驱动程序 [解决办法] 1.查看本地安装chrome浏览器版本 2. ...

  3. Git安装配置及第一次上传项目到GitHub

    平时的学习工作少不了保存自己的Code到代码库,这里必须要使用到Git与GitHub. 1.   关于Git的安装 下载Git:下载地址:https://git-scm.com/downloads  ...

  4. 伟大的悲剧——记CSP2019

    伟大的悲剧——记CSP2019 就算伟大,依然悲剧…… 现在我好恨自己 我……差一点……就是省一了…… 这一点是多少呢? 2分! 1名! 省一65人,我第66! 唉……太悲催了…… jx的分数线居然还 ...

  5. 保存为pickle

    保存为pickle import pickle # 保存模型 with open('model.pickle', 'wb') as f: pickle.dump(model, f) # 读取模型 wi ...

  6. 一个C语言程序是由( )组成?

    A) 一个主程序和若干子程序组成 B)一个或多个函数组成 C) 若干过程组成 D) 若干子程序组成 正确答案 B 解析 [解析] 一个C源程序是由一个main函数和若干个其他函数组成的.函数是C程序的 ...

  7. ES6之常用开发知识点:入门(一)

    ES6介绍 ES6, 全称 ECMAScript 6.0 ,2015.06 发版. let 和 const命令 let命令 let 命令,用来声明变量.它的用法类似于var,区别在于var声明的变量全 ...

  8. 基于topsis和熵权法

    % % X 数据矩阵 % % n 数据矩阵行数即评价对象数目 % % m 数据矩阵列数即经济指标数目 % % B 乘以熵权的数据矩阵 % % Dist_max D+ 与最大值的距离向量 % % Dis ...

  9. 将一个Head下的Line复制到另一个Head下(ef+linq)

    今天工作中有一个需求,要求将一个Item下的Line复制到另外一个Item下面 这个需求在工作中很多,按照以往的经验肯定是先delete from,然后再一条条遍历后insert into 这两天正好 ...

  10. 深度优先搜索DFS---求出矩阵中“块”的个数。

    题目: 给出一个 m x n 的矩阵,矩阵中的元素为0或1.如果矩阵中有若干个 1是相邻的,那么称这些1构成了一个“块”.求给定的矩阵中“块”的个数. 0 0 0 0 0 0 0 0 0 0 0 0 ...