题解:AtCoder Beginner Contest 262
D - I Hate Non-integer Number
·难度
普及/提高-
·题目
链接:D - I Hate Non-integer Number (atcoder.jp)
·思路
首先遍历一遍,一共取多少个数,之后每轮做一次dp,用f[j][k][l]表示考虑前j个数取其中k个并且该k个数的和除以i余l。其中我们依次枚举j、k、l,对于f[j][k][l]都可以用两种方式来求值(就是把这两种情况的f加起来):
①不选第j个数。用f[j-1][k][l](相当于只考虑前j-1个数,其他不变)更新。
②选择第j个数。用f[j-1][k-1][(l+i-a[j]%i)%i](这个比较复杂,是指考虑前j-1个数选k-1个【留出一个数给第j个数】,其中(l+i-a[j]%i)%i表示取第j个数之前除以i的余数就相当于刚刚取完第j个数除以i的余数把第j个数除以i的余数减去,需要注意的是减完之后可能是负数,所以要加上i)更新。
对于每一次遍历i,最终把答案加上f[n][i][0]。
时间复杂度O(n4)。
·代码
链接:Submission #42274509 - AtCoder Beginner Contest 262
#include<bits/stdc++.h>
#define N 110
#define P 998244353LL
using namespace std;
long long f[N][N][N]={},ans=0;
//其实用int也行,但是用longlong更稳妥
int a[N]={},n=0;
int main(){
scanf("%d",&n);
for(int i=1;i<=n;i++){
scanf("%d",&a[i]);
}
//输入
for(int i=1;i<=n;i++){
memset(f,0,sizeof(f));
//初始化f
f[0][0][0]=1;
//需要注意考虑前0个数取0个求和除以i余0(和本身就是0)有一种情况
for(int j=1;j<=n;j++){
//最多有n个数
for(int k=0;k<=j&&k<=i;k++){
//一共只考虑前j个数,不肯能出现第大于j个数,而且这轮dp不会出现取大于i个数
for(int l=0;l<i;l++){
f[j][k][l]=(f[j][k][l]+f[j-1][k][l])%P;
//第一种更新,注意要取模
if(k!=0){
f[j][k][l]=(f[j][k][l]+f[j-1][k-1][(l+i-a[j]%i)%i])%P;
//第二轮更新,除了取模还要注意“k-1”在k==0时是-1,所以会出现一些奇怪的问题
}
}
}
}
ans=(ans+f[n][i][0])%P;
//更新答案也要取模
}
printf("%lld\n",ans);
//打印答案
return 0;
}
·注意
①注意答案一定要取模(否则longlong也不行),而且必须每次操作取一次模,否则还没跑完就炸了。
②做第二种转移时一定要判断k是否为0,是0不用做也不能做(参见我坎坷的调试过程:Submission #42264726 - AtCoder Beginner Contest 262)
③每轮dp记得把f初始化成0。