1. 程式人生 > 實用技巧 >AtCoder agc009_c - Division into Two

AtCoder agc009_c - Division into Two

(hgs 風格小清新題解)

簡單 DP 題。咋這種題都 2400 啊。。。

注意到最終 \(A\)\(B\) 都各一定能表示成 \(s\)(有序)的若干個區間。

於是對這個區間 DP。

我們考慮 \(dp_{i,0/1}\) 為考慮劃分前 \(i\) 個,第 \(i\) 個分到 \(A/B\) 的方案數。

然後轉移的話,就 \(0\) 轉移到 \(1\) \(1\) 轉移到 \(0\),決策要滿足兩個限制:

  1. 當前區間內部滿足條件;
  2. 區間兩端要滿足條件。

然後隨便推。發現 1 的決策集合是個 \([1,i]\) 的字尾,即有下界;2 是個字首,即有上界。

綜合起來就是一個區間?字首和隨便維護一下。

1 可以用分段 for

預處理,2 傻逼寫 lower_bound,不傻逼發現單調性,two-pointers 隨便維護。

然後做完了?

#include<bits/stdc++.h>
using namespace std;
#define int long long
const int mod=1000000007,inf=0x3f3f3f3f3f3f3f3f;
const int N=100000;
int n,a,b;
int s[N+1];
int Sum[N+1][2];
int dp[N+1][2];
int lft[N+1][2];
int sum(int l,int r,bool tp){
	if(l>r)return 0;
	return (Sum[r][tp]-Sum[l-1][tp])%mod;
}
signed main(){
	cin>>n>>a>>b;
	for(int i=1;i<=n;i++)scanf("%lld",s+i);
	for(int i=1,ie;i<=n;i=ie+1){
		ie=i;while(ie+1<=n&&s[ie+1]-s[ie]>=a)ie++;
		for(int j=i;j<=ie;j++)lft[j][0]=i;
	}
	for(int i=1,ie;i<=n;i=ie+1){
		ie=i;while(ie+1<=n&&s[ie+1]-s[ie]>=b)ie++;
		for(int j=i;j<=ie;j++)lft[j][1]=i;
	}
	dp[0][0]=dp[0][1]=Sum[0][0]=Sum[0][1]=1;
	int las0=0,las1=0;
	s[0]=-inf,s[n+1]=inf;
	for(int i=1;i<=n;i++){
		while(las0+1<i&&s[las0+1]<=s[i+1]-a)las0++;
		while(las1+1<i&&s[las1+1]<=s[i+1]-b)las1++;
		dp[i][0]=sum(lft[i][0]-1,las1,1);
		dp[i][1]=sum(lft[i][1]-1,las0,0);
		Sum[i][0]=(Sum[i-1][0]+dp[i][0])%mod;
		Sum[i][1]=(Sum[i-1][1]+dp[i][1])%mod;
	}
	cout<<(dp[n][0]+dp[n][1]+10*mod)%mod;
	return 0;
}