271. 杨老师的照相排列
思路:
一道有点类似的题目
code:
#include<bits/stdc++.h>
#define endl '\n'
#define ll long long
#define ull unsigned long long
#define ld long double
#define all(x) x.begin(), x.end()
#define eps 1e-6
using namespace std;
const int maxn = 2e6 + 9;
const int mod = 1e9 + 7;
const int inf = 0x3f3f3f3f;
const ll INF = 0x3f3f3f3f3f3f3f3f;
ll n, m;
ll f[31][31][31][31][31];
// 合法集合的特征:a, b, c, d, e 严格非递减
// 转移时一定要保证转移前后的集合都是合法的
void work()
{
memset(f, 0, sizeof(f));
int s[5] = {0};
for(int i = 0; i < n; ++i) cin >> s[i];
f[0][0][0][0][0] = 1;
for(int a = 0; a <= s[0]; ++a)
for(int b = 0; b <= min(s[1], a); ++b)
for(int c = 0; c <= min(s[2], b); ++c)
for(int d = 0; d <= min(s[3], d); ++d)
for(int e = 0; e <= min(s[4], d); ++e)
{
ll &x = f[a][b][c][d][e];
if(a && a - 1 >= b) x += f[a-1][b][c][d][e];
if(b && b - 1 >= c) x += f[a][b-1][c][d][e];
if(c && c - 1 >= d) x += f[a][b][c-1][d][e];
if(d && d - 1 >= e) x += f[a][b][c][d-1][e];
if(e) x += f[a][b][c][d][e-1];
}
cout << f[s[0]][s[1]][s[2]][s[3]][s[4]] << endl;
}
int main()
{
ios::sync_with_stdio(0);
while(cin >> n && n)
work();
return 0;
}
如果没有对序列大小关系的限制,只需要考虑 a i a_i ai 应该放在第一个序列还是第二个序列,我们定义 f [ i ] [ j ] f[i][j] f[i][j] 表示前 i i i 个数,第二个序列放了 j j j 个,第一个序列放了 i − j i-j i−j 个的方案。但是又考虑到数字相同时,放的顺序可以任意,所以我们直接把相同的数字 x x x 缩成一个点,设共有 t t t 个 x x x,枚举分配给第一个序列 q q q 个 x x x ,分配给第二个序列 t − q t − q t−q 个 x x x,这样 d p dp dp 就没有考虑顺序,所以最后乘以一个 t ! t! t! 即可
现在考虑序列大小顺序的限制,现在要求第一个序列的对应位置不小于第二个序列,也就是任意时刻
j
>
=
i
−
j
j > = i − j
j>=i−j
这个限制比较巧妙,利用后面加进来的数字更大的特性完美满足了这个限制
d
p
dp
dp 数组的含义不变,但是所有
j
<
i
−
j
j < i − j
j<i−j 的状态都变成不合法状态
其实仔细观察转移方程,可以用数据结构优化区间和做到
O
(
n
l
o
g
(
n
)
)
O ( n l o g ( n ) )
O(nlog(n))
#include<bits/stdc++.h>
#define _ 0
using namespace std;
typedef long long ll;
const int maxn = 5e3 + 9;
const int mod = 998244353;
ll n, m, ans;
ll fac[maxn], a[maxn], pos[maxn];
ll f[maxn][maxn];
void work()
{
fac[0] = 1;
for(int i = 1; i <= 5000; ++i) fac[i] = fac[i-1] * i % mod;
cin >> n;
for(int i = 1; i <= n; ++i) cin >> a[i], ++pos[a[i]];
sort(a + 1, a + 1 + n);
vector <int> v;
for(int i = 1; i <= maxn - 9; ++i)
if(pos[i]) v.push_back(pos[i]);
f[0][0] = 1;
ll pre = 0;
for(auto x : v)
{
pre += x;
for(int j = min(pre, n / 2); j >= pre - j; --j)
for(int k = 0; k <= x && k <= j; ++k)
f[pre][j] = (f[pre][j] + f[pre - x][j - k]) % mod;
}
ans = f[n][n/2];
for(int i = 1; i <= n; ++i)
ans = (ans * fac[pos[i]]) % mod;
cout << ans;
}
int main()
{
ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);
//int TT;cin>>TT;while(TT--)
work();
return ~~(0^_^0);
}