gym-101982C 2018-2019 ACM-ICPC Pacific Northwest n个数中任意k个数之积

博客分析了ACM-ICPC Pacific Northwest竞赛中,从n个不同等级的题目中选取k道题的方案数问题。通过数学归纳,提出了计算任意k个数乘积的方法,涉及到多次前缀和的计算,最终得出解决方案是C[1]。
摘要由CSDN通过智能技术生成

分析

首先给了我们n个题目,每个题目都有一个等级,现在要求从k个等级中分别选出一道题,问方案数
我们可以想到,去统计每个等级中题目的个数,然后方案数为从所有等级中任选k个等级,并从每个选中的等级中选一道题。
假设最终有n个等级,则最终答案为n个数中任意k个数的乘积
如果 k = 1 k=1 k=1,我们会发现方案数是所有等级中题目数量之和
如果 k = 3 k=3 k=3,我们有:
∑ i = 1 n ∑ j = i + 1 n ∑ k = j + 1 n a [ i ] ∗ a [ j ] ∗ a [ k ] \sum_{i=1}^{n}\sum_{j=i+1}^{n}\sum_{k=j+1}^{n}a[i]*a[j]*a[k] i=1nj=i+1nk=j+1na[i]a[j]a[k]
∑ i = 1 n a [ i ] ∑ j = i + 1 n a [ j ] ∑ k = j + 1 n a [ k ] \sum_{i=1}^{n}a[i]\sum_{j=i+1}^{n}a[j]\sum_{k=j+1}^{n}a[k] i=1na[i]j=i+1na[j]k=j+1na[k]

我们令 A [ x ] = ∑ i = x n a [ i ] A[x]=\sum_{i=x}^{n}a[i] A[x]=i=xna[i]

∑ i = 1 n a [ i ] ∑ j = i + 1 n a [ j ] A [ j + 1 ] \sum_{i=1}^{n}a[i]\sum_{j=i+1}^{n}a[j]A[j+1] i=1na[i]j=i+1na[j]A[j+1]

我们令 B [ x ] = ∑ i = x n a [ i ] A [ i + 1 ] B[x]=\sum_{i=x}^{n}a[i]A[i+1] B[x]=i=xna[i]A[i+1]

∑ i = 1 n a [ i ] B [ i + 1 ] \sum_{i=1}^{n}a[i]B[i+1] i=1na[i]B[i+1]

我们令 C [ x ] = ∑ i = x n a [ i ] B [ i + 1 ] C[x]=\sum_{i=x}^{n}a[i]B[i+1] C[x]=i=xna[i]B[i+1]

最后我们发现答案为: C [ 1 ] C[1] C[1]

所以,n个数中任意k个之积:
相当于上面的推导进行k次,也就是进行k次前缀和

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
template <typename T>
void out(T x) { cout << x << endl; }
ll fast_pow(ll a, ll b, ll p) {ll c = 1; while(b) { if(b & 1) c = c * a % p; a = a * a % p; b >>= 1;} return c;}
ll exgcd(ll a, ll b, ll &x, ll &y) { if(!b) {x = 1; y = 0; return a; } ll gcd = exgcd(b, a % b, y, x); y-= a / b * x; return gcd; }
const int N = 1000 + 6;
const int mod = 998244353;
ll a[N];
ll sum[N], num[N];
int main()
{
    ios::sync_with_stdio(false);
    memset(sum, 0, sizeof(sum));
    memset(num, 0, sizeof(num));
    memset(a,0,sizeof(a));
    map <int, int> q;
    int n, k;
    cin >> n >> k;
    q.clear();
    int cnt=0;
    for(int i=1;i<=n;i++)
    {
        int x;
        cin >> x;
        if(q[x]==0)
        {
            q[x]=++cnt;
        }
        a[q[x]]++;
    }
    n = cnt;
    for(int dk = 1; dk <= k; dk ++)
    {
        for(int i = n; i >= 1; i --)
        {
            sum[i] = (sum[i + 1] + (dk == 1 ? a[i] : a[i] * num[i + 1] % mod) % mod + mod) % mod;
        }
        for(int i = 1; i <= n; i ++)
            num[i] = sum[i];
    }
    cout << sum[1] << endl;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值