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. opencv —— equalizeHist 直方图均衡化实现对比度增强

    直方图均匀化简介 从这张未经处理的灰度图可以看出,其灰度集中在非常小的一个范围内.这就导致了图片的强弱对比不强烈. 直方图均衡化的目的,就是把原始的直方图变换为在整个灰度范围(0~255)内均匀分布的 ...

  2. .NET CLI简单使用

    官方文档https://docs.microsoft.com/zh-cn/dotnet/core/tools/?tabs=netcore2x 创建新项目 查看能创建什么类型的项目 dotnet new ...

  3. Luarocks 安装艰难过程

    https://www.cnblogs.com/fanxiaojuan/p/11551268.html

  4. pytest文档32-allure描述用例详细讲解

    前言 pytest+allure是最完美的结合了,关于allure的使用,本篇做一个总结. allure报告可以很多详细的信息描述测试用例,包括epic.feature.story.title.iss ...

  5. 拓展lucas结论及模板

    lucas及其拓展 模板题 洛谷 P4720 本文侧向结论和代码实现, 推导请转至lucas定理及其拓展的推导 https://blog.csdn.net/yuyilahanbao/article/d ...

  6. 吴裕雄--天生自然HADOOP操作实验学习笔记:使用hive操作hbase

    实验目的 熟悉hive和hbase的操作 熟悉hadoop.hbase.hive.zookeeper的关系 熟练大数据环境的搭建 学会分析日志排除问题 实验原理 1.hive整合hbase原理 前面大 ...

  7. final关键字在JVM中的理解

    我们先来看两段代码与运行结果,根据结果去分析解释一下 不加final关键字: package com.waibizi; public class demo02 { public static void ...

  8. js获取页面缩放比例

    今天在网上看到一位大神写的一篇文章,出处记不得了,只是因为我在做项目的时候需要用到所以看了一眼. 经理要求我把两张图表上下排列(非响应式的)改成可以适配浏览器的,刚开始只是想改样式,看到代码才发现原来 ...

  9. P2048 [NOI2010]超级钢琴 [堆+st表]

    考虑只能取长度为 [L,R] 的,然后不难想到用堆搞. 搞个前缀和的st表,里面维护的是一个 最大值的位置 struct rmq { int mx[N][20] ; void qwq(int n) { ...

  10. 虚拟机NAT模式连接外网

    虚拟机三种联网方式: 一.NAT(推荐使用)                功能:①可以和外部网络连通    ②可以隔离外部网络 二.桥接模式                        功能:直接 ...