[CF1290F] Making Shapes

题目

题目描述
You are given nn pairwise non-collinear two-dimensional vectors. You can make shapes in the two-dimensional plane with these vectors in the following fashion:

Start at the origin (0, 0)(0,0) .
Choose a vector and add the segment of the vector to the current point. For example, if your current point is at (x, y)(x,y) and you choose the vector (u, v)(u,v) , draw a segment from your current point to the point at (x + u, y + v)(x+u,y+v) and set your current point to (x + u, y + v)(x+u,y+v) .
Repeat step 2 until you reach the origin again.
You can reuse a vector as many times as you want.

Count the number of different, non-degenerate (with an area greater than 00 ) and convex shapes made from applying the steps, such that the shape can be contained within a m \times mm×m square. Since this number can be too large, you should calculate it by modulo 998244353998244353 .

Two shapes are considered the same if there exists some parallel translation of the first shape to another.

A shape can be contained within a m \times mm×m square if there exists some parallel translation of this shape so that every point (u, v)(u,v) inside or on the border of the shape satisfies 0 \leq u, v \leq m0≤u,v≤m .

输入格式
The first line contains two integers nn and mm — the number of vectors and the size of the square ( 1 \leq n \leq 51≤n≤5 , 1 \leq m \leq 10^91≤m≤10
9
).

Each of the next nn lines contains two integers x_ix
i

and y_iy
i

— the xx -coordinate and yy -coordinate of the ii -th vector ( |x_i|, |y_i| \leq 4∣x
i

∣,∣y
i

∣≤4 , (x_i, y_i) \neq (0, 0)(x
i

,y
i

)


=(0,0) ).

It is guaranteed, that no two vectors are parallel, so for any two indices ii and jj such that 1 \leq i < j \leq n1≤i<j≤n , there is no real value kk such that x_i \cdot k = x_jx
i

⋅k=x
j

and y_i \cdot k = y_jy
i

⋅k=y
j

.

输出格式
Output a single integer — the number of satisfiable shapes by modulo 998244353998244353 .

输入输出样例
输入 #1复制
3 3
-1 0
1 1
0 -1
输出 #1复制
3
输入 #2复制
3 3
-1 0
2 2
0 -1
输出 #2复制
1
输入 #3复制
3 1776966
-1 0
3 3
0 -2
输出 #3复制
296161
输入 #4复制
4 15
-4 -4
-1 1
-1 -4
4 3
输出 #4复制
1
输入 #5复制
5 10
3 -4
4 -3
1 -3
2 -3
-3 -4
输出 #5复制
0
输入 #6复制
5 1000000000
-2 4
2 -3
0 -4
2 4
-1 -3
输出 #6复制
9248783
说明/提示
The shapes for the first sample are:

The only shape for the second sample is:

The only shape for the fourth sample is:

思路

因为是凸包,所以如果确定了每个向量的出现次数 c c c,那么这些向量的顺序也就确定了。
换言之,需要进行计数的就是满足下列条件的数组 c c c,其中 c i c_i ci 表示向量 i i i 的出现次数:
∑ i = 1 n c i x i = 0 , ∑ i = 1 n c i y i = 0 \sum_{i=1}^{n}c_ix_i=0,\sum_{i=1}^n c_iy_i=0 i=1ncixi=0,i=1nciyi=0

从常规的角度来设计状态的话无法绕过 ∑ c x \sum cx cx 这种形式的状态,所以就需要用到一种非常极端的优化方法。
枚举哪些 c c c 的二进制下第 k k k位为 1 1 1,那么就可以采用类似数位 d p dp dp 的办法,从低位向高位转移,只存下 ∑ c i x i 2 k \frac{\sum c_ix_i}{2^k} 2kcixi 这种形式的状态,而且因为不能转移的状态是不需要储存的,所以只需要保留 ∑ c i x i 2 k ≤ ∑ x i \frac{\sum c_ix_i}{2^k}\leq \sum x_i 2kcixixi 的状态,而这样的状态是只有 O ( n V ) O(nV) O(nV) 种的。再状压一下两维与 m m m 的关系就可了(后缀是否大于 m m m)。

代码

#include<bits/stdc++.h>
#define FOR(i,n) for(int i = 0; i < (n); i += 1)
using namespace std;
constexpr int mod = 998244353;
int x[5],y[5];
int px[32],nx[32],py[32],ny[32];
int dp[32][20][20][20][20][2][2];
void add(int &x,const int& y){
	x += y;
	if(x >= mod) x -= mod;
}
int main(){
	int n,m;
	cin >> n >> m;
	for(int i = 0; i < n; i += 1)
		cin >> x[i] >> y[i];
	for(int mask = 0; mask < (1 << n); mask += 1)
		for(int i = 0; i < n; i += 1)
			if((mask >> i) & 1){
				(x[i] > 0 ? px : nx)[mask] += abs(x[i]);
				(y[i] > 0 ? py : ny)[mask] += abs(y[i]);
			}
	dp[0][0][0][0][0][0][0] = 1;
	FOR(i,30)FOR(cpx,20)FOR(cnx,20)FOR(cpy,20)FOR(cny,20)
		FOR(bx,2)FOR(by,2)FOR(mask,1 << n)
		if(dp[i][cpx][cnx][cpy][cny][bx][by]){
		if(0)cout << i << " " << cpx << " " << cny << " " << cpy << " " << cny << " " << bx << " " << by << " " << dp[i][cpx][cnx][cpy][cny][bx][by] << endl;
		int npx = cpx + px[mask],
		nnx = cnx + nx[mask],
		npy = cpy + py[mask],
		nny = cny + ny[mask];
		if((npx ^ nnx) & 1) continue;
		if((npy ^ nny) & 1) continue;
		int cx = npx & 1,mx = (m >> i) & 1;
		int cy = npy & 1,my = (m >> i) & 1;
		add(dp[i + 1][npx >> 1][nnx >> 1][npy >> 1][nny >> 1]
			[cx != mx ? (cx > mx) : bx][cy != my ? (cy > my) : by],
			dp[i][cpx][cnx][cpy][cny][bx][by]);
	}
	cout << (dp[30][0][0][0][0][0][0] + mod - 1) % mod;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值