zoukankan      html  css  js  c++  java
  • luogu4345 [SHOI2015]超能粒子炮·改(组合数/Lucas定理)

    link

    输入(n,k),求(sum_{i=0}^k{nchoose i})对2333取模,10万组询问,n,k<=1e18

    注意到一个2333这个数字很小并且还是质数这一良好性质,我们可以根据Lucas定理优化式子

    为了方便,令(p=2333)

    (f(n,k)=sum_{i=0}^k{nchoose i})

    对于(iin[0,plfloorfrac kp floor)),根据lucas定理有({nchoose i}={n\%pchoose i\%p}{n/pchoose i/p})

    对于每一对((i\%p,i/p))都能惟一确定一个(i),根据乘法原理有

    (f(n\%p,p-1)*f(lfloorfrac np floor,lfloorfrac kp floor-1))

    对于(iin[plfloorfrac kp floor,k])则它们/p的值相同,根据Lucas定理可以化为({lfloorfrac np floorchooselfloorfrac kp floor}*f(n\%p,k\%p))

    所以(f(n,k)=f(n\%p,p-1)*f(lfloorfrac np floor,lfloorfrac kp floor-1)+{lfloorfrac np floorchooselfloorfrac kp floor}*f(n\%p,k\%p))

    先预处理0~p-1阶乘及其逆元,0~p-1里的组合数可以O(1)

    注意到在f的递推式中频繁用到了0~p-1内的f值,所以先O(p^2)处理这些f

    那么时间复杂度递推式就是(T(n,k)=T(lfloorfrac np floor,lfloorfrac kp floor)+log p),如果nk同阶,复杂度(O(Tlog nlog ^2p))好像是

    一开始复杂度写错了,最后6个点狂T。。。

    代码

    #include <cstdio>
    using namespace std;
    
    const int p = 2333;
    
    int fac[3000], inv[3000];
    int f[3000][3000];
    
    int qpow(int x, int y)
    {
    	int res = 1;
    	for (x %= p; y > 0; x = x * x % p, y >>= 1) if (y & 1) res = res * x % p;
    	return res;
    }
    
    int c(long long n, long long m)
    {
    	if (n < m || m < 0) return 0;
    	if (n < p && m < p) return fac[n] * inv[m] % p * inv[n - m] % p;
    	return c(n / p, m / p) * c(n % p, m % p) % p;
    }
    
    int work(long long n, long long k)
    {
    	if (n < p && k < p) return f[n][k];
    	return (c(n / p, k / p) * work(n % p, k % p) + work(n % p, p - 1) * work(n / p, k / p - 1)) % p;
    }
    
    int main()
    {
    	fac[0] = 1;
    	for (int i = 1; i < p; i++)
    		fac[i] = fac[i - 1] * i % p;
    	inv[p - 1] = qpow(fac[p - 1], p - 2);
    	for (int i = p - 1; i >= 1; i--)
    		inv[i - 1] = inv[i] * i % p;
    	for (int i = 0; i < p; i++)
    	{
    		f[i][0] = c(i, 0);
    		for (int j = 1; j < p; j++)
    			f[i][j] = (f[i][j - 1] + c(i, j)) % p;
    	}
    	int t; scanf("%d", &t);
    	while (t --> 0)
    	{
    		long long x, y;
    		scanf("%lld%lld", &x, &y);
    		printf("%d
    ", work(x, y));
    	}
    	return 0;
    }
    

    WA了好几发,define int long long过了后来发现计算C时候三个数乘一起就炸int了。。。

  • 相关阅读:
    智能家居——安全信息收集(一)
    PMP软件管理之路——站在巨人的肩膀上(一)
    Linux——信息收集(二)Nikto网页服务器扫描器
    XML约束(3)
    xml基本语法(2)
    xml概述(1)
    ajax接受json响应
    ajax接受json响应(讲义)
    json与xml的比较
    零碎小技能
  • 原文地址:https://www.cnblogs.com/oier/p/10302092.html
Copyright © 2011-2022 走看看