分析
因为题目要求最终形状为一个凸包,所以你只要确定了要选哪些向量以及每个向量用几次,最终形成的那个多边形就固定了。
所以只需考虑最终式子是什么,得到 \(\sum_{i=1}^nx_i=0\),\(\sum_{i=1}^ny_i=0\)。然后你画出图形后会发现横纵轴的最长扩展长度就是只为正的 \(x_i\) 或 \(y_i\) 之和,所以我们要求的就是正负 \(x\) 值相等且都小于 \(m\),\(y\) 值亦然。然后发现 \(n\) 很小,\(x_i\) 和 \(y_i\) 也很小,只有 \(m\) 和我们求出来的每个向量选择次数会很大,所以我们考虑用数位 dp 去拆分每个向量的选择次数,在选择过程中维护两个坐标轴的正负值大小关系,和正值总值与 \(m\) 的关系。
由低位向高位传递过程中维护以下几个值:\(p\) 代表到第几位,\(zx\) 代表低位的选择得到的值为正的 \(x_i\) 总和向本位传递的值,\(zy\) 同理,\(hx\) 代表负的 \(x_i\) 向本位传递的值,\(hy\) 同理,\(cx\) 代表低位上为正的 \(x\) 值是否大于 \(m\) 的低位,\(cy\) 同理。每到一层时枚举当前层各个 \(c_i\) 的子集,以 \(s\) 表示,若 \(s\) 二进制下第 \(i\) 位为 \(1\),则说明第 \(i\) 个向量要选 \(2^p\) 个。
在到达第 \(30\) 位时,显然 \(m\) 值是不可能有 \(2^{30}\) 的,所以我们进行判断,首先那四个代表当前位值的数必须为 \(0\)。然后低位的为正的 \(x\),\(y\) 值必须同样小于等于 \(m\),都满足的情况下我们才能返回 \(1\),视为该情况可行。同时我们发现还有一个要求没处理,就是 \(x\),\(y\) 为正为负的值需相等,这样才能回到起点,因此在低位向高位传递时,必须正值负值该位同为 \(1\) 或同为 \(0\) 时才可以向下一层找答案。具体细节可以看代码。
代码
#include<bits/stdc++.h>
using namespace std;
const int mod=998244353;
inline void read(int &res){
res=0;
int f=1;
char c=getchar();
while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();}
while(c>='0'&&c<='9')res=(res<<1)+(res<<3)+c-48,c=getchar();
res*=f;
}
int n,m;
int x[6],y[6];
int f[32][23][23][23][23][2][2];
int check(int x,int y,int z){//m当前位,比较值当前位
if(x^y)return x<y?1:0;//若不同,比较当前位的值
return z;//若相同,保留低位比较结果
}
int dp(int p,int zx,int zy,int hx,int hy,int cx,int cy){//题解中说了含义了
//cx为1代表x和大于m,cy同理
if(p==30)return (!zx&&!zy&&!hx&&!hy&&!cx&&!cy);
if(~f[p][zx][zy][hx][hy][cx][cy])return f[p][zx][zy][hx][hy][cx][cy];
int d=m>>p&1;//m的当前位,用于后面的比较
f[p][zx][zy][hx][hy][cx][cy]=0;
for(int s=0;s<(1<<n);s++){
int nwx=zx,nwy=zy,nwhx=hx,nwhy=hy;//存储当前以s的方式选择最终得到怎样的x值和,y值和
for(int i=1;i<=n;i++){
if(s>>(i-1)&1){
if(x[i]<0)nwhx-=x[i];
else nwx+=x[i];
if(y[i]<0)nwhy-=y[i];
else nwy+=y[i];
}
}
int dqx=nwx&1,dqy=nwy&1,dqhx=nwhx&1,dqhy=nwhy&1;//当前位正负xy值
if(dqx==dqhx&&dqy==dqhy){//若该位满足相等条件,向更高位寻找答案
f[p][zx][zy][hx][hy][cx][cy]=(f[p][zx][zy][hx][hy][cx][cy]+dp(p+1,nwx>>1,nwy>>1,nwhx>>1,nwhy>>1,check(d,dqx,cx),check(d,dqy,cy)))%mod;
}
}
return f[p][zx][zy][hx][hy][cx][cy];
}
int main()
{
read(n);read(m);
for(int i=1;i<=n;i++){
read(x[i]);read(y[i]);
}
memset(f,-1,sizeof(f));
printf("%d",(dp(0,0,0,0,0,0,0)-1+mod)%mod);
}