UOJ22. 【UR #1】外星人【DP】【思維】
阿新 • • 發佈:2018-11-06
題目大意
給你一個序列和一個值x
問你用某種方式對序列安排順序之後一次對x取mod膜的最大值和方案數
首先發現一個性質
- 一個數之後所有比它大的數都沒有貢獻
考慮怎麼利用這個性質?
就可以從小到大插入每一個數
然後就開開心心的發現每次插入的數如果有貢獻一定是在第一個,否則可以在任意位置
然後就可以非常自然地令\(f_{i,j}\)表示初始數是i,放入前j個數的最大值
然後轉移就是列舉當前有沒有貢獻\(f[i][j] = \max(f[i][j - 1], f[i\% a[j]][j - 1])\)
注意特判邊界
然後第一問就做完了
考慮第二問,\(g_{i,j}\)
每次直接判斷兩個值是一樣大還是一個比另一個更大,累加貢獻就可以啦
注意i是0也要算方案數哦!
//Author: dream_maker #include<bits/stdc++.h> using namespace std; //---------------------------------------------- typedef pair<int, int> pi; typedef long long ll; typedef double db; #define fi first #define se second #define fu(a, b, c) for (int a = b; a <= c; ++a) #define fd(a, b, c) for (int a = b; a >= c; --a) #define fv(a, b) for (int a = 0; a < (signed)b.size(); ++a) const int INF_of_int = 1e9; const ll INF_of_ll = 1e18; template <typename T> void Read(T &x) { bool w = 1;x = 0; char c = getchar(); while (!isdigit(c) && c != '-') c = getchar(); if (c == '-') w = 0, c = getchar(); while (isdigit(c)) { x = (x<<1) + (x<<3) + c -'0'; c = getchar(); } if (!w) x = -x; } template <typename T> void Write(T x) { if (x < 0) { putchar('-'); x = -x; } if (x > 9) Write(x / 10); putchar(x % 10 + '0'); } //---------------------------------------------- const int N = 5e3 + 10; const int Mod = 998244353; int n, x, a[N]; int f[N][N], g[N][N]; int add(int a, int b) { return (a += b) >= Mod ? a - Mod : a; } int mul(int a, int b) { return 1ll * a * b % Mod; } int main() { #ifdef dream_maker freopen("input.txt", "r", stdin); #endif Read(n), Read(x); fu(i, 1, n) Read(a[i]); sort(a + 1, a + n + 1); fu(i, 0, x) f[i][0] = i; fu(i, 0, x) fu(j, 1, n) { if (j == 1) f[i][j] = i % a[j]; else f[i][j] = max(f[i][j - 1], f[i % a[j]][j - 1]); } Write(f[x][n]), putchar('\n'); fu(i, 0, x) g[i][1] = 1; fu(i, 0, x) { fu(j, 2, n) { g[i][j] = 0; if (f[i][j - 1] >= f[i % a[j]][j - 1]) { g[i][j] = add(g[i][j], mul(g[i][j - 1], j - 1)); } if (f[i][j - 1] <= f[i % a[j]][j - 1]) { g[i][j] = add(g[i][j], g[i % a[j]][j - 1]); } } } Write(g[x][n]); return 0; }