AGC 009 C Division into Two 题解

89 篇文章 1 订阅

题目传送门
首先可以发现这是一个dp计数题。
题目可以转化成

有一个数组: b 1 , b 2 , b 3 , b 4 . . b n b_1,b_2,b_3,b_4..b_n b1,b2,b3,b4..bn满足 b i > b i − 1 b_i>b_{i-1} bi>bi1,你要给每一个数图上两种颜色: 0 / 1 0/1 0/1,设 c i c_i ci表示第i个球的颜色。你要使得 min ⁡ { ∣ b i − b j ∣ } ≥ A , ( c i = c j = 0 ) \min\{|b_i-b_j|\}\geq A,(c_i=c_j=0) min{bibj}A,(ci=cj=0) min ⁡ { ∣ b i − b j ∣ } ≥ B , ( c i = c j = 1 ) \min\{|b_i-b_j|\}\geq B,(c_i=c_j=1) min{bibj}B,(ci=cj=1)

我们考虑最后的涂色样子:肯定是一段0/1,一段1/0,一段0/1…最后也是一段0/1。
我们可以想到一种暴力的dp转移: d p i , j , k dp_{i,j,k} dpi,j,k表是考虑到第i个,上一个颜色为0的位置,上一个颜色为1的位置。
这样的做法是 O ( n 4 ) O(n^4) O(n4)的。
如果用线段树/BIT 优化可以做到 O ( n 3 ∗ l o g 2 ( n ) ) O(n^3*log_2(n)) O(n3log2(n))的。
显然过不去。
而且貌似空间也达到了 O ( n 3 ) O(n^3) O(n3)不可能更优了。
但是仔细考虑就会发现,我们只需要存储 j , k j,k j,k的信息,因为 i = max ⁡ ( j , k ) i=\max(j,k) i=max(j,k)
这样空间就可以变为 O ( n 2 ) O(n^2) O(n2)的了。
这让我想到了ABC 176的F题也有一个类似的思想。
这样的化时间复杂度是 O ( n 2 ∗ l o g 2 ( n ) ) O(n^2*log_2(n)) O(n2log2(n)),空间是 O ( n 2 ) O(n^2) O(n2)的。
都不行。
我们就需要另外想一个方法。
其实我们可以用 d p i , c o l dp_{i,col} dpi,col表示以i结尾的段的颜色是col。
转移也不难想:
d p i , c o l = ∑ d p i ′ , c o l ⊗ 1 dp_{i,col}=\sum dp_{i\prime,col \otimes1} dpi,col=dpi,col1
那么 i ′ i\prime i有什么条件呢?
首先可以发现由于i是一个结尾,所以 s i + 1 − s i ′ ≥ A / B s_{i+1}-s_{i\prime}\geq A/B si+1siA/B。然后必须 s i ′ + 1 . . . s i s_{i\prime+1}...s_i si+1...si每两个相邻的数之间 ≥ A / B \geq A/B A/B
后面的那个显然就可以预处理。前面的那个由于数组是单调的,所以可以two pointers来预处理。设其为 p i , c o l p_{i,col} pi,col
假设 f a r i , c o l far_{i,col} fari,col表示第i个位置图col往前最远可以到达的合法位置(相邻的两个数之间的差 ≥ A / B \geq A/B A/B )。
然后dp式子就可以改写:
d p i , c o l = ∑ i ′ = f a r i , c o l − 1 p i + 1 , c o l ⊗ 1 d p i , c o l ⊗ 1 dp_{i,col}=\sum_{i\prime=far_{i,col}-1}^{p_{i+1,col\otimes 1}} dp_{i,col\otimes1} dpi,col=i=fari,col1pi+1,col1dpi,col1
这个用BIT/线段树就ok了。
Code:

/*
{By GWj
*/
#pragma GCC optimize(2)
#include<bits/stdc++.h>
#define rb(a,b,c) for(int a=b;a<=c;++a)
#define rl(a,b,c) for(int a=b;a>=c;--a)
#define LL long long
#define IT iterator
#define PB push_back
#define II(a,b) make_pair(a,b)
#define FIR first
#define SEC second
#define FREO freopen("check.out","w",stdout)
#define rep(a,b) for(int a=0;a<b;++a)
#define SRAND mt19937 rng(chrono::steady_clock::now().time_since_epoch().count())
#define random(a) rng()%a
#define ALL(a) a.begin(),a.end()
#define POB pop_back
#define ff fflush(stdout)
#define fastio ios::sync_with_stdio(false)
#define R(a) cin>>a
#define R2(a,b) cin>>a>>b
#define check_min(a,b) a=min(a,b)
#define check_max(a,b) a=max(a,b)
using namespace std;
const int INF=0x3f3f3f3f;
typedef pair<int,int> mp;
/*}
*/
const int N=100000;
const int MOD=1e9+7;
class BIT{
	int bit[N+10]={0};
	int sum(int i){
		int s=0;
		while(i>0){
			s=(s+bit[i])%MOD;
			i-=i&(-i);
		}
		return s;
	}
	public:
	void add(int i,int x=1){
		while(i<=N){
			bit[i]=(bit[i]+x)%MOD;
			i+=i&(-i);	
		}
	}
	public:
		int query(int l,int r){
			if(l>r) return 0;
			return (sum(r)-sum(l-1)+MOD)%MOD;
		}
}bit[2];
int n;
LL a,b,s[100000+1];
int far[N+1][2],f[2];
/*
f_{i,0}=\sum f_{i\prime,1},(i\prime < i\ and \ s_{i+1}-s_{i\prime}>=A/B)
*/
int main(){
	fastio;
	cin>>n>>a>>b;
	rb(i,1,n)
		R(s[i]);
	rb(i,1,n){
		far[i][0]=far[i][1]=i;
		if(s[i]-s[i-1]>=a){
			far[i][0]=far[i-1][0];
		}
		if(s[i]-s[i-1]>=b){
			far[i][1]=far[i-1][1];
		}
		check_max(far[i][0],1);
		check_max(far[i][1],1);	
	}
	int p1=0,p0=0;
	rb(i,1,n){
		f[0]=f[1]=0;
		if(i!=n){
			while(s[i+1]-s[p0+1]>=a){
//				cout<<p0<<endl;
				p0++;	
			} 
			while(s[i+1]-s[p1+1]>=b){
//				cout<<p1<<endl;
				p1++;	
			} 
		}
		else{
			p0=p1=n;
		}
		int l,r;
		l=far[i][0]-1;
		r=p1;
		check_max(l,1);
		f[0]=bit[1].query(l,r);
		if(far[i][0]==1){
			f[0]++;
		}
		l=far[i][1]-1;
		r=p0;
		check_max(l,1);
		f[1]=bit[0].query(l,r);
		if(far[i][1]==1){
			f[1]++;
		}
		rep(j,2){
			bit[j].add(i,f[j]);
		}
	}
	cout<<(f[1]+f[0])%MOD<<endl;
	return 0;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值