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. asp.net网站作为websocket服务端的应用该如何写

    最近被websocket的一个问题困扰了很久,有一个需求是在web网站中搭建websocket服务.客户端通过网页与服务器建立连接,然后服务器根据ip给客户端网页发送信息. 其实,这个需求并不难,只是 ...

  2. npm安装Vue.js

    我之前是有安装过npm的 使用淘宝 NPM 镜像 $ npm install -g cnpm --registry=https://registry.npm.taobao.org 查看nmp版本 $ ...

  3. jquery二级导航

    效果图 直接放代码 <!DOCTYPE html> <html lang="en"> <head> <meta charset=" ...

  4. Android Intent用法总结

    Android中提供了Intent机制来协助应用间的交互与通讯,Intent负责对应用中一次操作的动作.动作涉及数据.附加数据进行描述,Android则根据此Intent的描述,负责找到对应的组件,将 ...

  5. python——面向对象(3),搬家具

    """date: 2020.2.9搬家具:将小于房子剩余面积的家具搬进房子1.定义家具类,房屋类""" class Furniture(): ...

  6. System.Text.Json 自定义Converter实现时间转换

    Newtonsoft.Json与System.Text.Json区别 在 Newtonsoft.Json中可以使用例如 .AddJsonOptions(options => { options. ...

  7. java 企业站源码 兼容手机平板PC 自适应响应式 SSM主流框架 freemaker 静态引擎

    前台: 支持四套模版, 可以在后台切换   系统介绍: 1.网站后台采用主流的 SSM 框架 jsp JSTL,网站后台采用freemaker静态化模版引擎生成html 2.因为是生成的html,所以 ...

  8. Java连载88-HashSet集合与hashCode方法重写

    一.Set集合 1.HashSet底层实际上是一个HashMap,HashMap底层采用了哈希表数据结构. 2.哈希表又称为散列表,哈希表底层是一个数组,这个数组中每一个元素是一个单向链表,每个单向链 ...

  9. 【python数据挖掘】批量爬取站长之家的图片

    概述: 站长之家的图片爬取 使用BeautifulSoup解析html 通过浏览器的形式来爬取,爬取成功后以二进制保存,保存的时候根据每一页按页存放每一页的图片 第一页:http://sc.china ...

  10. M5310-A 版本

         模块外表            型号                           BAND                          M5310-A MBRH0S04 +NB ...