Atcoder 124E Pass to Next

Problem Statement

(N) people called Person (1,2,…,N) are standing in a circle.

For each (1≤i≤N−1), Person (i)'s neighbor to the right is Person (i+1), and Person (N)'s neighbor to the right is Person (1).

Person (i) initially has (a_{i}) balls.

They will do the following procedure just once.

  • Each person chooses some (possibly zero) of the balls they have.
  • Then, each person simultaneously hands the chosen balls to the neighbor to the right.
  • Now, make a sequence of length (N), where the (i)-th term is the number of balls Person (i) has at the moment.

Let (S) be the set of all sequences that can result from the procedure. For example, when (a=(1,1,1)), we have (S={(0,1,2),(0,2,1),(1,0,2),(1,1,1),(1,2,0),(2,0,1),(2,1,0)})

Compute (∑_{x∈S}∏^{N}_{i=1}x_i), modulo 998244353.

Constraints

  • All values in input are integers.
  • (3≤N≤10^5)
  • (0≤a_i≤10^9)

Input

Input is given from Standard Input in the following format:

N
a1 a2 ⋯⋯ aN

Output

Print (∑_{x∈S}∏^{N}_{i=1}x_i), modulo 998244353.


Sample Input 1 Copy

Copy

3
1 1 1
Sample Output 1 Copy

Copy

1
  • We have (S={(0,1,2),(0,2,1),(1,0,2),(1,1,1),(1,2,0),(2,0,1),(2,1,0)})
  • (∑_{x∈S}∏^{N}_{i=1}x_i) is 11.

Sample Input 2 Copy

Copy

3
2 1 1
Sample Output 2 Copy

Copy

6

Sample Input 3 Copy

Copy

20
5644 493 8410 8455 7843 9140 3812 2801 3725 6361 2307 1522 1177 844 654 6489 3875 3920 7832 5768
Sample Output 3 Copy

Copy

864609205
  • Be sure to compute it modulo 998244353.

题目翻译

有n个人,环形排列,每个人有(a_i)个球,每个人同一事件给下一个传递任意数量自己的球。

(S)不相同的最终状态的集合,求(∑_{x∈S}∏^{N}_{i=1}x_i)

题目解析

首先是,考虑什么情况下两种不同的传递方案可以得到相同的结果

显然传递序列(1,1,2,2)(2,2,3,3)的最终状态是等价的

归纳得出:两次传递等价满足传递序列的差分序列相同,则最终状态等价

上面两个传递序列差分序列均为(-1,0,1,0)

如何除去等价的传递?如果传递序列,如果最小值大于0,那么一定存在更小的等价传递序列

故可以得出最终方案数:(prod_{i=1}^{n}{a_i+1}-prod_{i=1}^{n}{a_i})

但是原题并不是求最终方案数,但是最终方案数的计算可以启发:利用容斥原理排除等价的状态

第二个重点是答案需求的表达式,首先(∏^{N}_{i=1}x_i)可以表示为第(i)个人各自在自己最后拥有的(x_i)球中选一个的方案数

这样原题就转换为了分类讨论第(i)个人选的一个球分别是前面的人,还是自己的方案数。可以用动态规划解决

(f[i][j](j=0/1)),当(j=0)时,(f[i][0])表示第 (i)个人从自己原本有的球中选球,前 (i−1)个人选球的方案数(不考虑自己)。当(j=1)时,(f[i][1])表示第 (i)个人从前面的球中选球,前 (i)个人选球的方案数(考虑自己的方案数)

之所以区分(j=0)(j=1)不同来源的表达方式,是为了独立不同来源的球

同时注意:前一个人传多少给下一个人与下一个人传出多少之间没有关系。也就是说,传递都是同时发生的

这里设(S(n)=sum_{i=1}^n i=frac{n(n+1)}{2}) (g(n)=sum_{i=1}^{n}{i^2}={frac{n(n+1)(2n+1)}{6}})

(f[i][0]->f[i+1][0])因为(f[i][0])没有计算第(i)个人的方案,故(f[i+1][0]+=f[i][0]*S(a_i))

(f[i][1]->f[i+1][0])因为算上了第(i)个人方案,只需算上传球方案(f[i+1][0]+=f[i][1]*(a_i+1))

(f[i][0]->f[i+1][1])因为没有计算第(i)个人,故需要计算(i)(i+1)的方案,假如第(i)个人剩(k)个球,第(i+1)人拿(a_i-k)个球,总方案为(sum_{k=1}^{a_i}{k*(a_i-k)}=a_i*S(a_i)-g(a_i)),有(f[i+1][1]+=f[i][0]*(a_i*S(a_i)-g(a_i)))

(f[i][1]->f[i+1][1])只要计算第(i+1)个人从第(i)个人获得球的方案,(f[i+1][1]+=f[i][1]*S(a_i))

上面的转移已经体现了区分(j=0)方案不包括(i)自己和(j=1)方案包括(i)的作用,当第(i)个人选择自己原有的球,第(i+1)人选择(i)传递的球,那么显然此时两个人的方案应该一起计算。同时(i+1)选择(i)传递的球,(i)选择(i-1)传递的球

由于转移是一个环,(i=n)需要转移到(i=1)。则需要先钦定第1个人是选自己的球还是选第 n 个人传的球(初值设为1,另一个设为0),然后转移一圈。如果初始化钦定 $f[1][0]=1,f[1][1]=0 $ ,那么最终答案就是(f[1][0]-1),反过来是一样的。

最后考虑传球最小值不为0的容斥,在调用一次DP并在模拟传球过程中限制传球数不为0。将之前DP的答案减去约束后得到的即为满足不等价条件的方案数

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cmath>
using namespace std;
typedef long long ll;
int n,a[100005];
int Mod=998244353;
ll f[100005][2],inv2,inv3;
ll qpow(ll x,int k){
    ll res=1;
    while (k){
        if (k&1) res=res*x%Mod;
        x=x*x%Mod;
        k>>=1;
    }
    return res;
}
ll dp(int init,int zero){//init表示dp[1][]的初值,zero表示是否不允许0传递   
    memset(f,0,sizeof(f));
    f[1][0]=init;f[1][1]=init^1;
    for (int i=1;i<=n;i++){
        int to=i%n+1;
        ll s=(a[i]-zero+Mod)%Mod;
        //转移时,剩余值为a[i]不合法,因为剩余a[i]代表转移为0
        ll s1=s*(s+1)%Mod*inv2%Mod;
        f[to][0]=(f[to][0]+f[i][0]*s1%Mod)%Mod;
        f[to][0]=(f[to][0]+f[i][1]*(s+1)%Mod)%Mod;
        if (zero) s++;
        //因为对于f[to][1]的转移,传递值0的情况计数为0不会计入结果
        //to选的球是来自i的球,所以此处表示传递值的a[i]合法
        ll s2=s*(s+1)%Mod*inv2%Mod;
        ll s3=s*(s+1)%Mod*(s*2+1)%Mod*inv2%Mod*inv3%Mod;
        f[to][1]=(f[to][1]+f[i][0]*(s2*s%Mod-s3+Mod)%Mod)%Mod;
        f[to][1]=(f[to][1]+f[i][1]*s2%Mod)%Mod;
    }
    if (init) {
        f[1][0]-=1;
        return (f[1][0]+Mod)%Mod;
    }else {
        f[1][1]-=1;
        return (f[1][1]+Mod)%Mod;
    }
}
int main(){
    cin>>n;
    inv2=qpow(2,Mod-2);inv3=qpow(3,Mod-2);
    for (int i=1;i<=n;i++){
        scanf("%d",&a[i]);
    }
    cout<<((dp(0,0)+dp(1,0))%Mod-(dp(0,1)+dp(1,1))%Mod+Mod)%Mod;
}
原文地址:https://www.cnblogs.com/Y-E-T-I/p/15134553.html