好题推荐 CF1932F Feed Cats

Part.1 题意

有一个喂猫的游戏,这个游戏有 n n n 步,每步中会出现一些猫,可以选择喂 所有的猫一只猫也不喂。要求不能喂一只猫多次。

另外的,猫的出现是有规律的,一共有 m m m 只猫,第 i i i 只猫只出现在第 l i l_{i} li 到第 r i r_{i} ri 步中。

求出可以喂最多几只猫呢?

Part.2 题解

考虑使用 dp。

Part.2-1 状态定义

简单的想,我们可以做出如下定义:

int dp[N];//dp[i]表示喂前 i 只猫的方案数

但是,如果在第 i i i 步喂第 j j j 只猫,那么不保证没有在第 l j l_{j} lj 步到第 i − 1 i-1 i1 步喂过此猫。

于是我们修改定义:

int dp[N];//dp[i]表示喂前 i 只猫的方案数,保证可以在这一步喂猫

Part.2-2 转移方程

容易给出方程:

d p x = m a x ( d p x + 1 , d p 包含猫区间的最大右端点 + 1 + 喂猫个数 ) dp_{x}=max(dp_{x+1},dp_{包含猫区间的最大右端点+1}+喂猫个数) dpx=max(dpx+1,dp包含猫区间的最大右端点+1+喂猫个数)

边界条件: d p n + 1 = 0 dp_{n+1}=0 dpn+1=0

Part.2-3 实现转移

  • 要求 包含猫区间的最大右端点,可以使用线段树,每次将区间 [ l i , r i ] [l_{i},r_{i}] [li,ri] M a x ( r i ) Max(r_{i}) Max(ri)

  • 要求 喂猫个数,可以使用差分,每次将区间 [ l i , r i ] [l_{i},r_{i}] [li,ri] 1 1 1

介绍一下查分。

若要将区间 [ l , r ] [l,r] [l,r] x x x,若将数组 a a a 的第 l l l + x +x +x,第 r + 1 r+1 r+1 − x -x x,再求前缀和,可以发现,它和区间增加是相同的效果。

Part.3 代码

#include <bits/stdc++.h>
using namespace std;
#define int long long
#define mid (l+r)/2
#define ls id*2
#define rs id*2+1
#define N 4000010
int a[N],tag[N],dp[N],sum[N],n,m;
struct Seg_tree{
	void update(int id,int l,int r,int ql,int qr,int val){
		if (ql<=l&&r<=qr) {
			tag[id]=max(val,tag[id]);
			return ;
		}if (qr>mid) update(rs,mid+1,r,ql,qr,val);
		if (ql<=mid) update(ls,l,mid,ql,qr,val);
	}int ask(int id,int l,int r,int tid){
		if (l==r){
			return max(a[id],tag[id]); 
		}if (tid<=mid) return max(ask(ls,l,mid,tid),tag[id]);
		else return max(ask(rs,mid+1,r,tid),tag[id]);
	}void clear(int id,int l,int r){
		if (l==r){
			a[id]=0; tag[id]=0;
			return ;
		}clear(ls,l,mid);
		clear(rs,mid+1,r);
		a[id]=0; tag[id]=0;
	}
}t;
int dfs(int x){
	if (x==0) return 0; 
	if (x==n+1) return dp[x]=0;
	if (dp[x]!=-1) return dp[x];
	return dp[x]=max(dfs(t.ask(1,1,n,x))+sum[x],dfs(x+1));
}
signed main(){
	int tx; cin>>tx;
	while (tx--){
		cin>>n>>m;
		t.clear(1,1,n);
		for (int i=1; i<=n; i++) dp[i]=-1,sum[i]=0;
		for (int i=1; i<=m; i++){
			int l,r; cin>>l>>r;
			t.update(1,1,n,l,r,r+1);
			sum[l]++; sum[r+1]--;
		}for (int i=1; i<=n; i++) sum[i]+=sum[i-1];
		cout<<dfs(1)<<"\n";
	}
	return 0;
}
  • 17
    点赞
  • 18
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值