Loading

NOIP 模拟 $86\; \rm 乘法$

题解 \(by\;zj\varphi\)

先提出来所有偶数的 \(2\),那么所有的偶数就会变成一段从 \(1\) 开始的前缀和,继续递归下去计算.

问题转化成求 \(\Pi_{i=1}^{(n-1)/2}(2i+1)\)

将括号拆开,最多选 63 个 2,再多就会因为自然溢出而没有作用。

所以这里可以化为:从 \(n\) 个数里选 \(m\) 个数的所有方案数的乘积和。

\(g_{n,m}\) 表示从 \(n\) 个数中选 \(m\) 个,所有方案数的乘积总和,这样式子可以转化为:\(\sum_{i=0}^{63}g_{n,i}*2^i\)

\(g\) 时可以递推:\(g_{n,m}=n\times g_{n-1,m-1}+g_{n-1,m}\)

转化一下,\(g\) 表示没选的数的乘积:\(g_{n,m}=g_{n-1,m-1}+n\times g_{n-1,m}\)

发现给 \(n\)\(1\) 后就变成了第一类斯特林数 \(n+1 \brack n-m+1\)

这个数很大,但是发现 \(n-m+1\) 很小,所以圆排列中大于 \(1\) 的环很少。

预处理出 \(dp_{i,j}\) 表示 \(i\) 个数分成 \(j\) 个圆排列,每个圆排列至少大于 \(1\) 的方案数。

最后就是如何求逆元,发现偶数无法求逆元,所以将偶数除去所有 \(2\) 的因子,剩下的奇数利用欧拉定理求出即可。

Code
#include<bits/stdc++.h>
#define ri signed
#define pd(i) ++i
#define bq(i) --i
#define func(x) std::function<x>
namespace IO{
    char buf[1<<21],*p1=buf,*p2=buf;
    #define gc() p1==p2&&(p2=(p1=buf)+fread(buf,1,1<<21,stdin),p1==p2)?(-1):*p1++
    #define debug1(x) std::cerr << #x"=" << x << ' '
    #define debug2(x) std::cerr << #x"=" << x << std::endl
    #define Debug(x) assert(x)
    struct nanfeng_stream{
        template<typename T>inline nanfeng_stream &operator>>(T &x) {
            bool f=false;x=0;char ch=gc();
            while(!isdigit(ch)) f|=ch=='-',ch=gc();
            while(isdigit(ch)) x=(x<<1)+(x<<3)+(ch^48),ch=gc();
            return x=f?-x:x,*this;
        }
    }cin;
}
using IO::cin;
namespace nanfeng{
    #define FI FILE *IN
    #define FO FILE *OUT
    template<typename T>inline T cmax(T x,T y) {return x>y?x:y;}
    template<typename T>inline T cmin(T x,T y) {return x>y?y:x;}
    using ull=unsigned long long;
    static const int N=155;
    int T;
    ull A[N][N],dp[N][N],inv[N];
    auto fpow=[](ull x,ull y) {
        ull res=1ull;
        while(y) {
            if (y&1) res=res*x;
            x=x*x;
            y>>=1; 
        }
        return res;
    };
    auto C=[](ull n,int m) {
        ull res=1ull;
        int cnt=0;
        for (ri i(1);i<=m;pd(i)) {
            res*=inv[i];
            int k=i;
            while(!(k&1)) k>>=1,++cnt;
        }
        for (ull i(n-m+1);i<=n;pd(i)) {
            ull k=i;
            while(cnt&&!(k&1)) k>>=1,--cnt;
            res*=k;
        }
        return res;
    };
    auto calc=[](ull x) {
        --x;
        x>>=1;
        ull res=0;
        ++x;
        for (ri t(0);t<64&&t<x;pd(t)) {
            ull tmp=0;
            for (ri k(t);k<=t<<1&&k<=x;pd(k)) tmp+=C(x,k)*dp[k][k-t];
            res+=tmp<<t;
        }
        return res;
    };
    inline int main() {
        FI=freopen("multiplication.in","r",stdin);
        FO=freopen("multiplication.out","w",stdout);
        for (ri i(0);i<=N-5;pd(i)) {
            A[i][0]=1ull;
            for (ri j(1);j<=i;pd(j)) A[i][j]=A[i][j-1]*(i-j+1);
        }
        dp[0][0]=1ull;
        for (ri i(1);i<=N-5;pd(i))
            for (ri j(1);j<=i;pd(j))
                for (ri k(2);k<=i;pd(k)) dp[i][j]+=A[i-1][k-1]*dp[i-k][j-1];
        for (ri i(1);i<=150;pd(i)) {
            ull x=i;
            while(!(x&1)) x>>=1;
            inv[i]=fpow(x,(1ull<<63)-1ull);
        }
        cin >> T;
        for (ri i(1);i<=T;pd(i)) {
            ull x,c=0,ans=1ull;
            cin >> x;
            while(x) ans*=calc(x),x>>=1,c+=x;
            ans<<=(c%4);
            bool jud=false;
            for (ri t(15);~t;bq(t)) {
                ull tmp=ans>>(t*4)&15;
                if (tmp) jud=true;
                if (jud) putchar(tmp<10?tmp+'0':tmp-10+'A');
            }
            printf("\n");
        }
        return 0;
    }
}
int main() {return nanfeng::main();}
posted @ 2021-10-31 10:05  ナンカエデ  阅读(54)  评论(0编辑  收藏  举报