peiwenjun's blog 没有知识的荒原

CF1290F Making Shapes 题解

题目描述

给定 \(n\) 个向量 \(\overrightarrow{v_i}=(x_i,y_i)\) ,初始你在 \((0,0)\) ,执行以下操作直到回到\((0,0)\)

  • 选择一个向量 \(\vec v\) ,从当前位置 \(\vec u\) 走到 \(\vec u+\vec v\)

这样你会得到一个多边形,求有多少种方案能得到一个凸多边形,并且凸多边形可以放入 \(m\times m\) 的矩形内,对 \(998244353\) 取模。

定义两种方案不同,当且仅当选择的向量(可重)集合不同

数据范围

  • \(1\le n\le 5,1\le m\le 10^9\)
  • \(0\le|x_i|,|y_i|\le 4\),保证不存在两个向量平行。

时间限制 \(\texttt{5s}\) ,空间限制 \(\texttt{768MB}\)

分析

凸包是假的。

假设向量集合已知,为构成凸包,所有向量的相对顺序唯一确定。

记第 \(i\) 个向量在集合中出现 \(c_i\) 次,多边形的限制等价于:

\[\sum_{i=1}^nc_ix_i=\sum_{i=1}^nc_iy_i=0\\ \]

为满足凸多边形能放入\(m\times m\)的矩形,我们需要统计横纵坐标上的极差。

以横坐标为例,凸包在 \(x\) 轴上的投影一定是先增,再减,再增到零。

因此极差为 \(\sum_{i=1}^n[x_i>0]c_ix_i\) ,纵坐标同理。

于是目标变为求满足以下条件的数组 \(c_i\) 个数:

\[\begin{cases} \sum_{i=1}^nc_ix_i=0\\ \sum_{i=1}^nc_iy_i=0\\ \sum_{i=1}^n[x_i\gt 0]c_ix_i\le m\\ \sum_{i=1}^n[y_i\gt 0]c_iy_i\le m\\ \end{cases} \]

本质上还是一个高维背包问题,只不过 \(m\) 很大并且单个物体体积可以为负。

数位 \(\texttt{dp}\) ,每次决策 \(c_i\) 在二进制下第 \(s\) 位的值。

\(f_{s,i,j,k,l}\) 表示考虑 \(\ge s\) 的所有位, \(\sum c_tx_t=i\cdot 2^s,\sum c_ty_t=j\cdot 2^s\)\(m-\sum [x_t\gt 0]c_tx_t=k\cdot 2^s,m-\sum [y_t\gt 0]c_ty_t=l\cdot 2^s\) 的方案数。

注意到 \(|i|,|j|>20\) 时一定不成立,并且 \(k,l\ge 20\) 等价于无限制,因此状态数仅有 \(\mathcal O(30\times 40^2\times 20^2)\approx 2\cdot 10^7\)

转移 \(\mathcal O(2^n)\) 暴力枚举这一位的所有决策(\(c_i\in\{0,1\}\))即可。

时间复杂度 \(\mathcal O(2^n(nv)^4\log m)\) ,由于跑不满所以能过。

#include<bits/stdc++.h>
using namespace std;
const int v=20,mod=998244353;
int m,n,res;
int x[5],y[5],f[31][41][41][21][21];
inline void add(int &x,int y)
{
    if((x+=y)>=mod) x-=mod;
}
int main()
{
    scanf("%d%d",&n,&m);
    for(int i=0;i<n;i++) scanf("%d%d",&x[i],&y[i]);
    f[30][v][v][0][0]=1;
    for(int s=29;s>=0;s--)
        for(int i=0;i<=40;i++)
            for(int j=0;j<=40;j++)
                for(int k=0;k<=20;k++)
                    for(int l=0;l<=20;l++)
                    {
                        if(!f[s+1][i][j][k][l]) continue;
                        for(int t=0;t<1<<n;t++)
                        {
                            int a=2*(i-v),b=2*(j-v),c=2*k+(m>>s&1),d=2*l+(m>>s&1);
                            for(int u=0;u<n;u++)
                            {
                                if(!(t>>u&1)) continue;
                                a+=x[u],b+=y[u],c-=max(x[u],0),d-=max(y[u],0);
                            }
                            if(max(abs(a),abs(b))>v||min(c,d)<0) continue;
                            add(f[s][a+v][b+v][min(c,v)][min(d,v)],f[s+1][i][j][k][l]);
                        }
                    }
    for(int k=0;k<=20;k++) for(int l=0;l<=20;l++) add(res,f[0][v][v][k][l]);
    printf("%d\n",(res+mod-1)%mod);
    return 0;
}

posted on 2023-06-01 16:34  peiwenjun  阅读(19)  评论(0)    收藏  举报

导航