Description
这道题的题意是这道难读,大概就是给你n个商店,每个商店的重量为i的商品用ai表示,对于任意商店的a数列都是相同的,重量的范围为[1,10]
求购买方案总数为奇数的重量一共有多少种,答案取膜998244353
Input Format
多组数据,每组数据第一行一个整数n表示一共有n个商店,第二行10个整数表示数列a
Output Format
对于每组数据输出一行表示答案
Sample Input
1
1 2 3 4 5 6 7 8 9 10
2
1 0 0 0 0 0 0 0 0 0
100
1 1 1 1 0 0 0 0 0 0
Sample Output
Solution
打算学习一下god们的题解风格
这道题目,首先答案显然为${(1 + sumlimits_{i = 1}^{10} {{a_i}*{x^i}} )^n}$中系数为奇数的项有几个
那么我们只要对该式进行处理就行了
首先我们设关注到题目要求统计的是系数为奇数,那么相当于在mod 2意义下进行运算
我们现在来考虑一个子问题,$f{left( n ight)^{2k}}*gleft( n ight)$中有几个系数为奇数
对于该式,我们意识到前一个式子是${(f{left( n ight)^k})^2}$因为是平方,所以打开之后两项相乘的系数就会消掉
例如${left( {a + b} ight)^2} = {a^2} + {b^2} + 2ab$最后一项对答案显然没有贡献,也就是说平方后我们关心的只是一个与原串相同的串
对于g(n),我们将其拆分成g(n)=o(n)+e(n),o(n)为g(n)中奇数次方的项,e(n)为g(n)中偶数次方的项,由于${(f{left( n ight)^k})^2}$只剩下平方项,所以o(n)与e(n)对答案的贡献是独立的
那么最后一步就是递归拆分后递归求解这个问题,顺便加个map瞎记忆化一下就过了
为什么我们要进行这个拆分呢?
我们可以很快发现这样拆分之后递归求解的时候o(n)和e(n)的项数就可以从20降为10,这样就可以将一个${10^{10}}$项的多项式希望得到的结果,只用10位的二进制数得到答案
题解是这么想,代码也确实是这么打,但是这个代码还是比较巧妙的(我本来以为要fft的,后来看了标程才明白,确实我觉得不用开ll,大家可以尝试一下)
代码啦~~~
#include<cstdio> #include<map> #include<algorithm> #define ll long long using namespace std; map<pair<ll,ll>,ll> mp; ll mo=998244353,x; ll mul(ll a,ll b){ ll ret=0; while (b){ ret^=a*(b&(-b)),b-=b&(-b); } return ret; } ll f(ll n,ll g){ if (mp.count(make_pair(n,g)))return mp[make_pair(n,g)]; ll &ret=mp[make_pair(n,g)]; if (!n)return ret=__builtin_popcountll(g)%mo; if ((n&1))g=mul(g,x);ll a=0,b=0; for (int i=0;i<=25;i++)if ((g&(1<<i))){ if ((i&1))a|=(1<<(i>>1)); else b|=(1<<(i>>1)); } return ret=(f(n>>1,a)+f(n>>1,b))%mo; } int a[15],n; int main(){ while (~scanf("%d",&n)){ mp.clear(); x=1; for (int i=1;i<=10;i++){ scanf("%d",&a[i]); if ((a[i]&1))x|=(1<<i); } ll ans=f(n,1); printf("%d ",ans); } }