[CF1290F]Making Shapes

Making Shapes

源码

相当有趣的数位 d p dp dp题。

我们记 c i c_i ci表示第 i i i个向量选了多少个。
显然,无论我们如何选择向量,只要我们将所有的向量极角排序,就可以得到这些向量构造的唯一凸多边形,当然需要满足 ∑ c i ( x i , y i ) = 0 \sum c_i(x_i,y_i)=0 ci(xi,yi)=0
整个凸包的宽度应该 L = ∑ c i [ x i > 0 ] ∣ x i ∣ = ∑ c i [ x i < 0 ] ∣ x i ∣ L=\sum c_i[x_i>0]|x_i|=\sum c_i[x_i<0]|x_i| L=ci[xi>0]xi=ci[xi<0]xi,高度同理。
我们需要让这个值小于 m m m,且等式两边的值相等。

我们不妨记 A = ∑ c i [ x i > 0 ] ∣ x i ∣ , B = ∑ c i [ x i < 0 ] ∣ x i ∣ , C = ∑ c i [ y i > 0 ] ∣ y i ∣ , D = ∑ c i [ y i < 0 ] ∣ y i ∣ A=\sum c_i[x_i>0]|x_i|,B=\sum c_i[x_i<0]|x_i|,C=\sum c_i[y_i>0]|y_i|,D=\sum c_i[y_i<0]|y_i| A=ci[xi>0]xi,B=ci[xi<0]xi,C=ci[yi>0]yi,D=ci[yi<0]yi
我们可以将 c c c二进制拆分后来统计,这样的话我们就能够一位一位得比较 A , B , C , D A,B,C,D A,B,C,D
由于 A , B , C , D A,B,C,D A,B,C,D可能即使只在当前位加,也有可能得到比较大的结果,我们显然不能只存 1 1 1位,得多存几位。
这样一来,如果从高位向低位枚举,每一位就有可能用满 5 5 5位,空间比较大。
我们可以从低位向高位枚举,这样每一位上最多只需要记录 3 ( 4 + 4 + 3 + 3 ) 2 = 21 \frac{3(4+4+3+3)}{2}=21 23(4+4+3+3)=21了,空间也能承受。
但由于是低位向高位,还需要注意下面的部分是否超过了 m m m的低位部分,如果超过了,高位就必须小于 m m m的高位了。
d p i , A , B , C , D , f x , f y dp_{i,A,B,C,D,f_x,f_y} dpi,A,B,C,D,fx,fy表示 c c c的二进制为枚举到了第 i i i位,四个值分别为 A , B , C , D A,B,C,D A,B,C,D,关于低位超过 m m m的情况是 f x , f y f_x,f_y fx,fy
可以用记忆化搜索更新 d p dp dp值,每一位转移就枚举 c c c该位的状态就行了。

时间复杂度 O ( 2 1 4 × 2 2 log ⁡   m ) O\left(21^4\times 2^2\log\,m\right) O(214×22logm)

源码

#include<bits/stdc++.h>
using namespace std;
#define MAXN 100005
#define lowbit(x) (x&-x)
#define reg register
#define pb push_back
#define mkpr make_pair
#define fir first
#define sec second
typedef long long LL;
typedef unsigned long long uLL; 
typedef long double ld;
typedef pair<int,int> pii;
const int INF=0x3f3f3f3f;
const int mo=998244353;
const int mod=1e9+7;
const int inv2=5e8+4;
const int jzm=2333;
const int zero=100000;
const int n1=1000;
const int lim=100000000;
const int orG=3,ivG=332748118;
const double Pi=acos(-1.0);
const double eps=1e-6;
template<typename _T>
_T Fabs(_T x){return x<0?-x:x;}
template<typename _T>
void read(_T &x){
	_T f=1;x=0;char s=getchar();
	while(s>'9'||s<'0'){if(s=='-')f=-1;s=getchar();}
	while('0'<=s&&s<='9'){x=(x<<3)+(x<<1)+(s^48);s=getchar();}
	x*=f;
}
template<typename _T>
void print(_T x){if(x<0){x=(~x)+1;putchar('-');}if(x>9)print(x/10);putchar(x%10+'0');}
int gcd(int a,int b){return !b?a:gcd(b,a%b);}
int add(int x,int y,int p){return x+y<p?x+y:x+y-p;}
void Add(int &x,int y,int p){x=add(x,y,p);}
int qkpow(int a,int s,int p){int t=1;while(s){if(s&1)t=1ll*t*a%p;a=1ll*a*a%p;s>>=1;}return t;}
int n,m,dp[35][23][23][23][23][2][2],a[10],b[10];
int sakura(int id,int px,int py,int nx,int ny,bool fx,bool fy){
	if(id==30)return px==nx&&py==ny&&(px+fx<=((m>>id)&15))&&(py+fy<=((m>>id)&15));
	if(~dp[id][px][py][nx][ny][fx][fy])return dp[id][px][py][nx][ny][fx][fy];
	int res=0,up=(m>>id)&1;
	for(int i=0;i<(1<<n);i++){
		int tpx=px,tpy=py,tnx=nx,tny=ny;bool ffx=fx,ffy=fy;
		for(int j=1;j<=n;j++)if((i>>j-1)&1){
			if(a[j]>0)tpx+=a[j];else tnx-=a[j];
			if(b[j]>0)tpy+=b[j];else tny-=b[j];
		}
		if((tpx&1)^(tnx&1))continue;if((tpy&1)^(tny&1))continue;
		if(up<(tpx&1))ffx=1;if(up>(tpx&1))ffx=0;
		if(up<(tpy&1))ffy=1;if(up>(tpy&1))ffy=0;
		Add(res,sakura(id+1,tpx>>1,tpy>>1,tnx>>1,tny>>1,ffx,ffy),mo);
	}
	dp[id][px][py][nx][ny][fx][fy]=res;
	return res;
}
signed main(){
	read(n);read(m);memset(dp,-1,sizeof(dp));
	for(int i=1;i<=n;i++)read(a[i]),read(b[i]);
	printf("%d\n",add(sakura(0,0,0,0,0,0,0),mo-1,mo));
	return 0;
}

谢谢!!!

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值