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 i−1 步喂过此猫。
于是我们修改定义:
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;
}