UOJ#22 外星人

来源:互联网 发布:英雄无敌3 mac 10.11 编辑:程序博客网 时间:2024/05/02 02:01

思路:
考虑一个简单的性质,若ai<ai+1,则(x mod aimod ai+1 = x mod ai
根据这个可以发现一个有效的取模序列的a是递减的,我们可以将a从大到小排序,设f[i][j]为考虑到i当前能否为j,存在两种转移但注意到min(ai)是一定有效的,那么必须强制选取否则会造成整个序列都不选的情况。
再来看第二问,考虑固定一个有效序列(单调递减),那么它对应多少种方案呢?
通过简单的组合数学知识可以发现为(n1)!Πi(loci1)其中loci为有效序列的位置
其中一个很蛋疼的问题就是重复元素,那么我们要让这个有效单调递减,而这些重复元素是等价的,我们枚举哪一个是有效元素,其它的就被抛到它后面去了,所以我们要在方案中乘上重复元素的个数,最后注意到还是要强制选取最小值即可。

代码:

#include<iostream>#include<cstring>#include<string>#include<cstdio>#include<algorithm>#define N 1000 #define C 5000using namespace std;typedef long long LL;int n,a[N + 5],P,ans,inv[N + 5],fact,x,F[C + 5],sum,f[N + 5];inline int getnum(){    char c;int num,flag = 1;    while (!isdigit(c = getchar()))      if (c == '-') flag = -1;    num = c - '0';    while (isdigit(c = getchar())) num = 10 * num + c - '0';    return num * flag;}inline void init(){    n = getnum(); x = getnum();    for (int i = 1;i <= n; ++i) a[i] = getnum();    sort(a + 1,a + n + 1);    P = 998244353;    fact = 1;    for (int i = 1;i < n; ++i) fact = 1LL * fact * i % P;    inv[0] = inv[1] = 1;    for (int i = 2;i <= n; ++i) inv[i] = 1LL * inv[P % i] * (P - P / i) % P;}inline void DO_IT(){    memset(f,0,sizeof(f));    memset(F,0,sizeof(F));    F[x] = 1;    int j,i = n;    while (i){        j = i;        while (a[j] == a[i]) --j;        for (int k = 0;k <= x; ++k)          if (F[k])          if (j)          {          F[k % a[i]] = (F[k % a[i]] + 1LL * F[k] * inv[j] % P * (i - j) % P);          if (F[k % a[i]] >= P) F[k % a[i]] -= P; }          else {  f[k % a[i]] = (f[k % a[i]] + 1LL * F[k] * inv[j] % P * (i - j) % P);              if (f[k % a[i]] >= P) f[k % a[i]] -= P;          }        i = j;    }    ans = -1;    for (int i = a[1] - 1;i >= 0;--i)      if (f[i]) {         ans = i; break;      } }int main(){    init();    DO_IT();    cout<<ans<<endl<<1LL * f[ans] * fact % P;    return 0;}

总结:1.考虑将解集合计数

0 0