zoukankan      html  css  js  c++  java
  • [提高组集训2021] 乘法

    一、题目

    (n!) 转成 (16) 进制后除去末尾 (0) 的最后 (16) 位。

    (n<2^{64},Tleq 10)

    二、解法

    首先考虑暴力怎么打,我们把所有 (2) 的因子提出来之后,剩下的数直接暴力乘法之后自然溢出即可,最后 (2) 的因子数模 (4) 之后乘上去,转成 (16) 进制输出即可。

    瓶颈在于把所有数乘起来,设 (f(n)=sum_{i=1}^n(2i+1)),那么答案是 (prod_{i=0}^{63}f((lfloorfrac{n}{2^i} floor-1)/2)),问题转化为怎么求 (f(n)),因为本题是在模 (2^{64}) 意义下的,所以选 (2i) 这一项的次数是 (leq 63) 的,要不然就被模成 (0) 了。

    (g(x)) 表示选了 (x)(2i)(i) 之积,转移考虑加入一个数,但是这个数可能和前面重复所以需要容斥,这里我们容斥最后一个数只出现一次这个条件,然后就足以进入子问题了,我们枚举出现次数 (j)

    [xcdot g(x)=sum_{j=1}^x (-1)^{j-1}cdot g(x-j)cdot sum_{iin[1,n]}i^j ]

    最后是一个幂次求和的结构,可以套路地第二类斯特林数反演(直接):

    [sum_{i=1}^n i^k=sum_{i=0}^k{krace i} imes frac{1}{i+1} imesfrac{(n+1)!}{(n-i)!} ]

    时间复杂度 (O(log^3n)),至于模意义下的除法可以奇数使用逆元,偶数就一直除 (2) 即可。

    还有多项式平移倍增的做法,上次省选学过我就不想复读了

    三、总结

    本题 (dp) 加容斥的做法让人耳目一新,两两不同的限制可以被我们拆分成每个数和其他数不同的限制,在我们考虑最后一个数是只用解决它的限制就可以归纳到子问题。

    #include <cstdio>
    #include <iostream>
    using namespace std;
    const int M = 1000;
    #define int unsigned long long
    int T,n,inv[M],S[M][M],tmp[M],g[M];
    void init(int n)
    {
    	//get inv about mod=2^64
    	for(int i=1;i<=n;i++)
    	{
    		inv[i]=1;int nw=i,b=(1ull<<63)-1;
    		while(b) {if(b&1)inv[i]*=nw;nw*=nw;b>>=1;}
    	}
    	//get S(n,m)
    	S[0][0]=1;
    	for(int i=1;i<=n;i++)
    		for(int j=1;j<=i;j++)
    			S[i][j]=S[i-1][j-1]+j*S[i-1][j];
    }
    int div(int x,int y)
    {
    	while(y%2==0) x/=2,y/=2;
    	return x*inv[y];
    }
    int work(int n)
    {
    	if(n==0) return 1;
    	n=(n-1)/2;
    	for(int i=0;i<=63;i++) tmp[i]=g[i]=0;
    	for(int i=1;i<=63 && i<=n;i++)
    	{
    		int A=n+1;
    		for(int j=1;j<=i;j++)
    		{
    			A=A*(n+1-j);
    			tmp[i]+=div(A,j+1)*S[i][j];
    		}
    	}
    	g[0]=1;
    	for(int i=1;i<=63 && i<=n;i++)
    	{
    		for(int j=1;j<=i;j++)
    		{
    			if(j&1) g[i]+=g[i-j]*tmp[j];
    			else g[i]-=g[i-j]*tmp[j];
    		}
    		g[i]=div(g[i],i);
    	}
    	int res=0;
    	for(int i=0;i<=63;i++) res+=g[i]<<i;
    	return res;
    }
    signed main()
    {
    	freopen("multiplication.in","r",stdin);
    	freopen("multiplication.out","w",stdout);
    	cin>>T;init(64);
    	while(T--)
    	{
    		cin>>n;int c=0,ans=1;
    		for(int i=1;i<=63;i++) c+=n>>i;
    		for(int i=0;i<=63;i++) ans*=work(n>>i);
    		for(int i=1;i<=c%4;i++) ans*=2;
    		printf("%llX
    ",ans);
    	}
    }
    
  • 相关阅读:
    css clear
    设置 系统软件
    Canvas 生成 bitmap
    HashTable
    JSF
    android 自定义 锁屏
    java socket
    php mysql 配置
    mysql 启动方法
    jQuery 表格
  • 原文地址:https://www.cnblogs.com/C202044zxy/p/15485478.html
Copyright © 2011-2022 走看看