Codeforces Round #620 (Div. 2) F2. Animal Observation (矩阵滑框+线段树)

第二次在Codeforces上做F题了,菜是原罪啊。。

F2. Animal Observation (hard version)

The only difference between easy and hard versions is the constraint on kk.

Gildong loves observing animals, so he bought two cameras to take videos of wild animals in a forest. The color of one camera is red, and the other one's color is blue.

Gildong is going to take videos for nn days, starting from day 11 to day nn. The forest can be divided into mm areas, numbered from 11 to mm. He'll use the cameras in the following way:

  • On every odd day (11-st, 33-rd, 55-th, ...), bring the red camera to the forest and record a video for 22 days.
  • On every even day (22-nd, 44-th, 66-th, ...), bring the blue camera to the forest and record a video for 22 days.
  • If he starts recording on the nn-th day with one of the cameras, the camera records for only one day.

Each camera can observe kk consecutive areas of the forest. For example, if m=5m=5 and k=3k=3, he can put a camera to observe one of these three ranges of areas for two days: [1,3][1,3], [2,4][2,4], and [3,5][3,5].

Gildong got information about how many animals will be seen in each area on each day. Since he would like to observe as many animals as possible, he wants you to find the best way to place the two cameras for nn days. Note that if the two cameras are observing the same area on the same day, the animals observed in that area are counted only once.

Input

The first line contains three integers nn, mm, and kk (1≤n≤501≤n≤50, 1≤m≤2⋅1041≤m≤2⋅104, 1≤k≤m1≤k≤m) – the number of days Gildong is going to record, the number of areas of the forest, and the range of the cameras, respectively.

Next nn lines contain mm integers each. The jj-th integer in the i+1i+1-st line is the number of animals that can be seen on the ii-th day in the jj-th area. Each number of animals is between 00 and 10001000, inclusive.

Output

Print one integer – the maximum number of animals that can be observed.

链接:http://codeforces.com/problemset/problem/1304/F2

题意:n*m矩阵,每行找一点为左上角,画2*k矩阵,求所包含的数字和最大值。

题解:矩阵滑框+线段树查询

很容易知道是个dp,第i行继承第i-1行最优解,在第i行j位置继承i-1行最优解时,并不知道在i-1行哪个位置才是最优解,所以要把i-1行dp[i-1][ ]遍历一边找最优解,然后i行j位置最优解就是:dp[i][j]=max(dp[i-1][ ])+(以[i,j]为左上角的矩阵和),然后会发现可能会和i-1行有重合部分,就要减去这个重合部分dp[i][j]=max(dp[i-1][ ])+(以[i,j]为左上角的矩阵和)-(重合部分);接着i行矩阵往右移动(矩阵滑框问题),枚举嘛肯定要试一下 i 行到底在哪里,产生i 行的最优解,到了新 j 位置,还要重复操作的把dp[i-1][ ]遍历一遍得到max(dp[i-1][ ]),所以要思考整体操作,唯一最费时间的地方就是在遍历上一行的最优解来dp继承,在dp[i][j]时遍历上一行最优解时,只是部分dp[i-1][ ]矩阵要减去重合部分,在dp[i][j+1]遍历上一行最优解时,减去的重合部分,这里面有些数据并没有修改,并且修改的数据也都是有因果关系的,这个因果关系具有唯一性,只需要用线段树来修改(减去i-1行重合部分的dp),在i行做矩阵滑框,并求滑框最大值dp[i][j]=(线段是维护的最大值)+(以[i,j]为左上角的矩阵和)。

(有位著名程序员说过:“少废话,让我看你写的代码”)

#include <iostream>
using namespace std;
int a[55][20005];
int sum[55][20005];//行前缀和 
int dp[55][20005]; 
int n,m,k;
struct node
{
	int x;
	int lazy;
}tree[20005*4];
void pushup(int i)
{
	tree[i].x=max(tree[i+i].x,tree[i+i+1].x);
}
void updown(int i)
{
	tree[i+i].x+=tree[i].lazy;
	tree[i+i+1].x+=tree[i].lazy;
	tree[i+i].lazy+=tree[i].lazy;
	tree[i+i+1].lazy+=tree[i].lazy;
	tree[i].lazy=0;
}
void build(int i,int l,int r,int top)
{
	tree[i].lazy=tree[i].x=0;
	if(l==r)
	{
		tree[i].x=dp[top][l];
		tree[i].lazy=0;
		return;
	}
	int mid=(l+r)/2;
	build(i+i,l,mid,top);
	build(i+i+1,mid+1,r,top);
	pushup(i);
}
void update(int i,int l,int r,int ll,int rr,int v)
{
	if(ll<=l&&r<=rr)
	{
		tree[i].x+=v;
		tree[i].lazy+=v;
		return;
	}
	if(tree[i].lazy)
	updown(i);
	int mid=(l+r)/2;
	if(ll<=mid)
	update(i+i,l,mid,ll,rr,v);
	if(mid<rr)
	update(i+i+1,mid+1,r,ll,rr,v);
	pushup(i);
}
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];
		}
	}
	for(int j=1;j+k-1<=m;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++)
	{
		build(1,1,m,i-1);//dp		
		
		for(int j=1;j<=k;j++)
		{
			update(1,1,m,max(1,j-k+1),j,-a[i][j]);
		}
		dp[i][1]=tree[1].x+sum[i][1+k-1]-sum[i][1-1]+sum[i+1][1+k-1]-sum[i+1][1-1];
		for(int j=2;j+k-1<=m;j++)
		{
			int top1=j+k-1;
			int top2=j-1;
			update(1,1,m,max(1,top1-k+1),top1,-a[i][top1]);//删前面值 
			update(1,1,m,max(1,top2-k+1),top2,a[i][top2]);//加后面值 
			dp[i][j]=tree[1].x+sum[i][j+k-1]-sum[i][j-1]+sum[i+1][j+k-1]-sum[i+1][j-1];
		}
	}
	int maxsum=0;
	for(int j=1;j<=m;j++)
	{
		maxsum=max(maxsum,dp[n][j]);
	}
	cout<<maxsum<<endl;
}

 

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值