[SPOJ]SELTEAM - Selecting Teams

原题链接

题意

大致意思:
\(n\)个人中选取\(k\)个人组成一个球班,然后在这个球班里面任选人数组成球队,再在球队里面选取一名队长,求方案数。

分析

就一句话:给定\(n,k\)求下面式子的值\(\sum{(C_n^i \times (\sum{(C_i^p \times p)}))}\)
发现\(T,n,k\)都巨大,所以考虑每次查询的复杂度应该是\(O(logn)\)
阶乘预处理阶乘和阶乘逆元。
这样子我们每次询问就是\(O(n^2)\)了,总复杂度\(O(TN^2)\)
但是好像预处理出inv[3]==0。。。
啥玩意啊????
等等…
模数好像不是质数。。。

想了半天没有思路,然后只好跑去看题解。。。
我们发现先选好球班和队长之后,其他人再考虑要不要加入球队。
那么我们有一个新的计算方法\(\sum{(c_n^i*i*2^(i-1)),1\le i\le k}\)
然后就是这道神仙题神仙的地方了。
不看题解我一辈子都想不出来。
玄机在模数上:\(8388608=2^{23}\)
所以\(n-i\ge 23\)的部分我们都不用算了。。。
我们大力组合数只要开\(10^5\times 23\)的辅助空间就可以艹过去了。。。。。

代码

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <cmath>
#include <queue>
#include <vector>
#define ull unsigned long long
#define ll long long
using namespace std;
const int ti=1e4+100,N=1e5;
const int mod=8388608;
ll read(){
    char c;ll num,f=1;
    while(c=getchar(),!isdigit(c))if(c=='-')f=-1;num=c-'0';
    while(c=getchar(), isdigit(c))num=num*10+c-'0';
    return f*num;
}
ll n,k,C[N+1000][30];
ll ans,tmp;
void init(){
    for(int i=0;i<=N+100;i++){
        C[i][0]=1;
        for(int j=1;j<=min(23,i);j++)
            C[i][j]=(C[i-1][j]+C[i-1][j-1])%mod;
    }
}
void add(ll &a,ll b){
    a+=b;
    if(a>=mod)a-=mod;
}
void work(){
    n=read();k=read();ans=0;
    for(int i=1;i<=min(k,23ll);i++)
        add(ans,C[n][i]*i%mod*(1<<(i-1))%mod);
    printf("%lld\n",ans);
}
int main()
{
    init();
    int Case=read();
    while(Case--)work();
    return 0;
}

转载于:https://www.cnblogs.com/onglublog/p/9913075.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值