uoj22 外星人

题意:给你一个数列ai,设d经过a的一个排列顺次对ai取模最后得到y。

求使得|d-y|最小的y,以及可以达到y的排列数%998244353。

ai,x<=5000,n<=1000.

标程:

 1 #include<cstdio>
 2 #include<algorithm>
 3 using namespace std;
 4 typedef long long ll;
 5 const int mod=998244353;
 6 const int N=1005;
 7 int n,d,f[N][N*5],a[N];
 8 bool cmp(int A,int B){return A>B;}
 9 int main()
10 {
11     scanf("%d%d",&n,&d);
12     for (int i=1;i<=n;i++) scanf("%d",&a[i]);
13     sort(a+1,a+n+1,cmp);
14     f[0][d]=1;
15     for (int i=1;i<=n;i++)
16     {
17         for (int j=0;j<=d;j++) f[i][j]=((ll)f[i][j]+(ll)f[i-1][j]*(n-i)%mod)%mod;//注意j有可能等于d。 
18        for (int j=0;j<=d;j++) f[i][j%a[i]]=((ll)f[i][j%a[i]]+f[i-1][j])%mod; 
19     }
20     for (int i=a[n]-1;i>=0;i--) if (f[n][i]) return printf("%d
%d
",i,f[n][i]),0;
21 }

题解:dp

分析后可以发现,如果排列中一个ai前面有aj比它小,那么这个ai就是无用的。

反之,如果前面所有的aj都比ai大,那么ai就是关键点。

先把所有元素从大到小排序。

我们将元素从小到大加入,设当前加入的数是ai。那么之后的n-i个都已经加入,并且都比ai要小。那么ai是关键点当且仅当ai插入在全部n-i个小于ai的点之前。反之ai有n-i个间隔可插入且不是关键点。

设f[i][j]为%前i个数(从大到小排序的前i个)后余数为j的方案数。

1.ai作为关键点:f[i][j%a[i]]+=f[i-1][j];经过前i-1个数后对ai取模。

2.ai为非关键点:f[i][j]+=(n-i)*f[i-1][j]。有n-i个间隔可供插入,这个点无效就不必考虑。

时间复杂度O(nd)。

原文地址:https://www.cnblogs.com/Scx117/p/8693851.html