【CCPC2020长春站】【区间dp】Abstract Painting

Description:

Once there was a painter, expert in drawing circles on a canvas. As a tasteful painter, he found that if he drew circles according to the following restrictions, the resulting canvas would look like an abstract painting.

The center of any circle must be a grid point on the x-axis.
The x-coordinate of any point on any circle must be within the range [0,n].
The radius of any circle must be a positive integer not exceeding 5.
Any two circles have at most 1 intersection point.
Specially, an empty canvas was also considered an abstract painting, as it broke none of the above restrictions.

There had already been k circles drawn on the canvas, not breaking the above restrictions. The painter could further draw one or more circles on the canvas to get an abstract painting, or he could draw nothing and claim that it was already an excellent abstract painting. He wondered how many different abstract paintings he could possibly get. As the number may be very large, you only need to output it modulo 1 0 9 + 7 10^9+7 109+7.

Input:

The first line contains two integers n and k ( 2 ≤ n ≤ 1 0 3 , 0 ≤ k ≤ 5 n 2≤n≤10^3, 0≤k≤5n 2n103,0k5n).

If k>0, each of the next k lines contains two integers c and r ( 1 ≤ c < n , 1 ≤ r ≤ ⌊ n 2 ⌋ 1≤c<n,1≤r≤⌊n^2⌋ 1c<n,1rn2), indicating that there was already a circle centering at (c,0) with radius r on the plane.

It is guaranteed that the drawn k circles satisfy the above restrictions.

Output:

Print the number of different abstract paintings the painter might get, modulo 1 0 9 + 7 10^9+7 109+7.

【简要题意】

给定一个长度为n的数轴,在数轴上面会放置一些区间。要求这些区间不能相互交叉,区间长度仅能为2、4、6、8、10。现在,数轴上已经放置了一些区间,保证这些区间符合限制,求在此基础上继续放置一些区间的方案数。

【思路】

由于放置的区间长度非常小,因此这道题的主流做法是状态压缩dp。下面,博主将会介绍一种区间dp的做法,这种做法虽然代码上比状态压缩dp复杂,但是可以更好地处理区间长度比原题数据规模更大时的情况。
状态定义:
首先,由于区间互不交叉,因此实际上放置的所有区间可以构成一个森林的结构,理解这一点是理解状态转移的前提。定义 d p [ l ] [ r ] [ 0 / 1 ] dp[l][r][0/1] dp[l][r][0/1]表示在区间 [ l , r ] [l,r] [l,r]内放置一些区间的方案数, 0 / 1 0/1 0/1表示 [ l , r ] [l,r] [l,r]本身是否是放置的一个区间(从森林的角度来说,0表示在 [ l , r ] [l,r] [l,r]放置出一个森林,1表示在 [ l , r ] [l,r] [l,r]放置一棵树)。
状态转移:
考虑转移,为了统计的不重不漏,我们仅枚举下一个放置的区间是谁。在这里,如果我们要在 [ l , r ] [l,r] [l,r]放置出一颗以 [ l , r ] [l,r] [l,r]为根节点的树,那么定义下一个区间是当前区间 [ l , r ] [l,r] [l,r]的最左侧的“儿子”节点区间;如果要在 [ l , r ] [l,r] [l,r]放置出一个森林,那么定义下一个区间是最左侧的树的“根”节点区间。对于当前区间 [ l , r ] [l,r] [l,r],我们用一个vector保存已经放置的且对 [ l , r ] [l,r] [l,r]有影响的区间,具体而言,是保存所有满足 l ≤ a < b ≤ r l\le a <b\le r la<br的已经放置的区间 [ a , b ] [a,b] [a,b]。设 S = S= S={ 2 , 4 , 6 , 8 , 10 2,4,6,8,10 2,4,6,8,10},那么我们可以构造转移:
d p [ l ] [ r ] [ 1 ] = ∑ l ≤ x < y ≤ r , y − x ∈ S , ( x , y ) ≠ ( l , r ) d p [ x ] [ y ] [ 1 ] ∗ d p [ y ] [ r ] [ 0 ] dp[l][r][1]=\sum_{l\le x<y\le r,y-x\in S,(x,y)\neq(l,r)}dp[x][y][1]*dp[y][r][0] dp[l][r][1]=lx<yr,yxS,(x,y)=(l,r)dp[x][y][1]dp[y][r][0]
d p [ l ] [ r ] [ 0 ] = ∑ l ≤ x < y ≤ r , y − x ∈ S d p [ x ] [ y ] [ 1 ] ∗ d p [ y ] [ r ] [ 0 ] dp[l][r][0]=\sum_{l\le x<y\le r,y-x\in S}dp[x][y][1]*dp[y][r][0] dp[l][r][0]=lx<yr,yxSdp[x][y][1]dp[y][r][0]
边界条件:
d p [ x ] [ x ] [ 0 / 1 ] = 1 dp[x][x][0/1]=1 dp[x][x][0/1]=1
d p [ x ] [ x + 1 ] [ 0 / 1 ] = 1 dp[x][x+1][0/1]=1 dp[x][x+1][0/1]=1
d p [ x ] [ x + 2 ] [ 0 ] = 2 , d p [ x ] [ x + 2 ] [ 1 ] = 1 dp[x][x+2][0]=2,dp[x][x+2][1]=1 dp[x][x+2][0]=2,dp[x][x+2][1]=1
需要注意的是, ( x , y ) (x,y) (x,y)除了满足上面的条件,还需要满足vector里保存的区间 [ a i , b i ] [a_i,b_i] [ai,bi]要么落在 [ x , y ] [x,y] [x,y]内部,要么落在 [ y , r ] [y,r] [y,r]内部。
时间复杂度 O ( 玄学 ) O(玄学) O(玄学),个人估计是常数极小的 O ( n 3 ) O(n^3) O(n3),空间复杂度 O ( n 2 ) O(n^2) O(n2)
算法实现:
用记忆化搜索实现上述算法,容易使枚举的区间 [ x , y ] [x,y] [x,y] [ y , r ] [y,r] [y,r]继承 [ l , r ] [l,r] [l,r]的vector里的已放置区间然后递归处理,降低枚举已放置区间的时间复杂度。博主个人认为,区间dp与状压dp相比,虽然就本题而言,容易超时且细节较多,但是区间dp可以在区间长度没有限制的情况下仍然在多项式时间复杂度内解决这个问题,区间dp的做法更具有一般性。

【代码】

#include<bits/stdc++.h>
#define re register
#define F(i,a,b) for(int re i=a;i<=b;i++)
#define D(i,a,b) for(int re i=a;i>=b;i--)
#define ll long long
#define mp make_pair
using namespace std;
const int N=1e3+5,mod=1e9+7;
inline int mul(int x,int y){
	return 1ll*x*y%mod;
}
inline int add(int x,int y){
	return x+y>=mod?x+y-mod:x+y;
}
inline void Add(int&x,int y){
	((x+=y)>=mod)&&(x-=mod);
}
int n,m,a[N],dp[N][N][2];
inline int red(){
	char ch=getchar();
	int data=0,w=0;
	while(ch!='-'&&(ch<'0'||ch>'9'))ch=getchar();
	if(ch=='-')ch=getchar(),w=1;
	while('0'<=ch&&ch<='9')data=(data<<1)+(data<<3)+ch-'0',ch=getchar();
	return w?-data:data;
}
struct node{
	int l,r;
};
int dfs(int l,int r,vector<node>q){
	bool ar=0;
	for(node t:q){
		if(t.l==l&&t.r==r)ar=1;
	}
	if(~dp[l][r][ar])return dp[l][r][ar];
	if(l+2>=r){
		if(r-l==2)return (!ar)+1;
		return 1;
	}
	int ans=(q.size()<=ar);
	for(int i=l;i<=r;i++){
		for(int j=2;j<=10&&i+j<=r;j+=2){
			if(ar&&i==l&&i+j==r)continue;
			if((i!=l)||(i+j!=r)){
				bool flag=0;
				for(node t:q){
					if(t.l==l&&t.r==r)continue;
					if(t.l>=i&&t.r<=i+j)continue;
					if(t.l>=i+j&&t.r<=r)continue;
					flag=1;
				}
				if(flag)continue;
			}
			vector<node>lq,rq;
			for(node t:q){
				if(t.l==l&&t.r==r)continue;
				if(t.r<=i+j&&((t.l!=i)||(t.r!=i+j)))lq.push_back(t);
				if(t.l>=i+j)rq.push_back(t);
			}
			lq.push_back((node){i,i+j});
			int x,y;
			Add(ans,mul(x=dfs(i,i+j,lq),y=dfs(i+j,r,rq)));
		}
	}
	return dp[l][r][ar]=ans;
}
vector<node>q;
int main(){
	memset(dp,-1,sizeof dp);
	n=red();m=red();
	F(i,1,m){
		int c=red(),r=red();
		q.push_back((node){c-r,c+r}); 
	}
	cout<<dfs(0,n,q)<<"\n";
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值