ARC120F Wine Thief (组合数学)
阅读原文时间:2023年07月10日阅读:3

题面

有一个长为

N

N

N 的序列,相邻的两个数中只能选一个,总共选

k

k

k 个数,一种方案的价值为选的

k

k

k 个数的和,问所有合法方案的价值总和,答案对 998244353 取模。

1

N

3

1

0

5

,

1

k

N

2

1\leq N\leq 3\cdot10^5~,~1\leq k\leq \left\lceil \frac{N}{2} \right\rceil

1≤N≤3⋅105 , 1≤k≤⌈2N​⌉.

题解

把每个数的贡献拆开求,答案就是 每个数的值 × 该数被选的方案数。这是最基本的转化。

G

(

n

,

i

,

k

)

G(n,i,k)

G(n,i,k) 表示

n

n

n 个数中选

k

k

k 个数,必选第

i

i

i 个数的方案数。

我们先鲁莽地列个式子,枚举第

i

i

i 个数前面选了多少个数,然后就是个放球问题,不难发现

G

(

n

,

i

,

k

)

=

j

=

0

(

i

1

)

/

2

C

i

1

j

j

C

n

i

k

1

+

j

k

1

j

G(n,i,k)=\sum_{j=0}^{(i-1)/2}C_{i-1-j}^{j}\cdot C_{n-i-k-1+j}^{k-1-j}

G(n,i,k)=∑j=0(i−1)/2​Ci−1−jj​⋅Cn−i−k−1+jk−1−j​

——毫无可做性。

我们得换个思路想。不妨容斥一下?先设

f

(

n

,

k

)

f(n,k)

f(n,k) 为在

n

n

n 个数中选

k

k

k 个数的总方案数,可得

f

(

n

,

k

)

=

C

n

k

+

1

k

f(n,k)=C_{n-k+1}^{k}

f(n,k)=Cn−k+1k​。

然后这里利用了官解里所说的 “ 化环 ” 的思想。

如果把整个序列视为一个环(即第一个点和最后一个点不能同时选),那么

G

(

n

,

1

,

k

)

=

G

(

n

,

2

,

k

)

=

=

G

(

n

,

n

,

k

)

G'(n,1,k)=G'(n,2,k)=\cdots=G'(n,n,k)

G′(n,1,k)=G′(n,2,k)=⋯=G′(n,n,k),不妨令此时的

G

(

n

,

1

,

k

)

=

G

(

n

,

2

,

k

)

=

=

G

(

n

,

n

,

k

)

=

F

(

n

,

k

)

G'(n,1,k)=G'(n,2,k)=\cdots=G'(n,n,k)~=~F(n,k)

G′(n,1,k)=G′(n,2,k)=⋯=G′(n,n,k) = F(n,k),则通过一番组合推导,可以发现

F

(

n

,

k

)

=

{

n

<

3

:

[

k

=

=

1

]

n

3

:

f

(

n

3

,

k

1

)

F(n,k)=\bigg\lbrace{\begin{matrix}n<3:[k==1]~~~\\ n\geq3:f(n-3,k-1) \end{matrix}}

F(n,k)={n<3:  [k==1]         n≥3:f(n−3,k−1)​

如果不是个环,那就多了一种情况:第一个点和最后一个点同时选,我们把它加上去就行了。选了第一个点和最后一个点后,相当于中间的长度为

n

4

n-4

n−4 的子区间又构成了一个子问题,因此

G

(

n

,

i

,

k

)

G(n,i,k)

G(n,i,k) 有这样的递推公式:

G

(

n

,

i

,

k

)

=

{

i

0

:

0

i

=

1

:

F

(

n

,

k

)

+

f

(

n

4

,

k

2

)

i

>

1

:

F

(

n

,

k

)

+

G

(

n

4

,

i

2

,

k

2

)

i

>

n

2

:

G

(

n

,

n

i

+

1

,

k

)

G(n,i,k)=\begin{cases} i\leq0: & 0\\ i=1: & F(n,k)+f(n-4,k-2)\\ i>1: & F(n,k)+G(n-4,i-2,k-2)\\ i>\lceil\frac{n}{2}\rceil: & G(n,n-i+1,k) \end{cases}

G(n,i,k)=⎩⎪⎪⎪⎨⎪⎪⎪⎧​i≤0:i=1:i>1:i>⌈2n​⌉:​0F(n,k)+f(n−4,k−2)F(n,k)+G(n−4,i−2,k−2)G(n,n−i+1,k)​

我们将一般情况(

i

>

1

i>1

i>1)展开,可以得到:

G

(

n

,

i

,

k

)

=

F

(

n

,

k

)

+

F

(

n

4

,

k

2

)

+

F

(

n

8

,

k

4

)

+

G(n,i,k)=F(n,k)+F(n-4,k-2)+F(n-8,k-4)+\cdots

G(n,i,k)=F(n,k)+F(n−4,k−2)+F(n−8,k−4)+⋯

不难发现对于不同的

i

i

i ,前面部分都是一样的,

i

i

i 只决定式子长度以及最后一项!

那么我们就可以从左到右遍历

i

i

i 的时候把

G

(

n

,

i

,

k

)

G(n,i,k)

G(n,i,k) 衔接着求,只求前一半。每次到新的

i

i

i 最多会增加或减少一两项。由于递推式中每次

i

i

i 会减少 2,且

i

=

1

i=1

i=1 属于特殊情况,所以要分奇偶性讨论。

具体实现有点细节,但是只求前一半,再考虑考虑边界应该就没问题了。

时间复杂度

O

(

n

)

O(n)

O(n)。

CODE

#include<cstdio>
#include<vector>
#include<cmath>
#include<queue>
#include<map>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
#define MAXN 1000005
#define DB double
#define LL long long
#define ENDL putchar('\n')
#define lowbit(x) ((-x) & (x))
#define INF 0x3f3f3f3f
LL read() {
    LL f=1,x=0;char s = getchar();
    while(s < '0' || s > '9') {if(s=='-')f = -f;s = getchar();}
    while(s >= '0' && s <= '9') {x=x*10+(s-'0');s = getchar();}
    return f * x;
}
const int MOD = 998244353;
int n,m,i,j,s,o,k;
int a[MAXN];
int fac[MAXN],inv[MAXN],invf[MAXN];
inline int qkpow(int a,int b) {
    int res = 1;
    while(b > 0) {
        if(b & 1) res = res *1ll* a % MOD;
        a = a *1ll* a % MOD; b >>= 1;
    }return res;
}
inline int C(int n,int m) {
    if(m < 0 || m > n) return 0;
    return 1ll*fac[n] * invf[n-m] % MOD *invf[m] % MOD;
}
inline int f_(int n,int k) {return C(n-k+1,k);}
inline int F(int n,int k) {
    if(n <= 3) {
        if(k == 1) return 1;
        else return 0;
    }
    else return f_(n-3,k-1);
}
int tm[MAXN];
int main() {
    n = read();k = read();int D = read();
    int ans = 0;
    fac[0] = fac[1] = inv[0] = inv[1] = invf[1] = invf[0] = 1;
    for(int i = 2;i <= n;i ++) {
        fac[i] = fac[i-1] *1ll* i % MOD;
        inv[i] = (MOD-inv[MOD%i]) *1ll* (MOD/i) % MOD;
        invf[i] = invf[i-1] *1ll* inv[i] % MOD;
    }
    for(int i = 1;i <= n;i ++) {
        a[i] = read(); tm[i] = -1;
    }
    int ans0 = 0,ans1 = f_(n,k),ad1 = 0,ad2 = 0;
    for(int i = 1;i <= n;i ++) {
        if(i & 1) {
            (ans1 += MOD-f_(n-4*ad1,k-2*ad1)) %= MOD;
            (ans1 += F(n-4*ad1,k-2*ad1)) %= MOD;
            ad1 ++;
            (ans1 += f_(n-4*ad1,k-2*ad1)) %= MOD;

            if(tm[i] >= 0) ans1 = tm[i];
            else tm[i] = tm[n-i+1] = ans1;
            (ans += ans1 *1ll* a[i] % MOD) %= MOD;
        }
        else {
            (ans0 += F(n-4*ad2,k-2*ad2)) %= MOD; ad2 ++;

            if(tm[i] >= 0) ans0 = tm[i];
            else tm[i] = tm[n-i+1] = ans0;
            (ans += ans0 *1ll* a[i] % MOD) %= MOD;
        }
    }
    printf("%d\n",ans);
    return 0;
}

手机扫一扫

移动阅读更方便

阿里云服务器
腾讯云服务器
七牛云服务器

你可能感兴趣的文章