【hdu多校联考第二场】Odd Shops

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

  6
  2
  35

 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);
    }
}

 

原文地址:https://www.cnblogs.com/Cool-Angel/p/9380809.html