Address
洛谷P3352
BZOJ4574
UOJ#196
LOJ#2093
Solution
原本是一个期望 DP ,但是由于乘上了
,变成了计数 DP ,即求每个位置每种情况下的值之和。
数据是随机的,假设它们互不相同。先离散化。
很容易想到一个状态:
表示第
个数变成从小到大第
个数的方案数。
那么
位置的答案为:
其中
为从小到大第
个数的值。
考虑如何求
。如果有
,那么无论如何操作, 区间
之外的值永远不会变成
。
其中
为
左边第一个比
大的数的位置(不存在则为
)
为
右边第一个比
大的数的位置(不存在则为
)
所以再定义状态
表示第
轮之后恰好 区间
变成了
的方案数。
这样还是不好 (ke) 推。因为如果某次操作发生在
(
),那么
会增加或者
会减小(设分别改变至
和
),这样如果操作的左端点在
而右端点大于等于
则修改对
的分布会特别难处理。
于是我们把状态中的
和
改为
和
的值,即:
表示第
轮之后恰好区间
小于等于
的方案数。
边界:
转移(1):操作区间不包含
:
其中
表示与区间
没有交集的区间个数。
转移(2):操作区间
(
)使得
的区间
缩小到
:
转移(3):操作区间
(
)使得
的区间
缩小到
:
转移(2)(3)需要前缀和优化。
同样地重新设置
的定义:
表示
轮后第
个数
的方案数。
对于每个
(
为
的排名):
同样可以前缀和优化。
然后将
数组差分之后就得出
位置为
的方案数。
注意细节:如果对于一个位置
和一个
,如果有
,且
不包含
,则
会为
,而这样显然是不对的。所以,差分时应该枚举一个
,对于所有的满足
且
包含
的
取出并差分。
复杂度
。
Code
#include <cmath>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#define For(i, a, b) for (i = a; i <= b; i++)
#define Rof(i, a, b) for (i = a; i >= b; i--)
using namespace std;
inline int read()
{
int res = 0; bool bo = 0; char c;
while (((c = getchar()) < '0' || c > '9') && c != '-');
if (c == '-') bo = 1; else res = c - 48;
while ((c = getchar()) >= '0' && c <= '9')
res = (res << 3) + (res << 1) + (c - 48);
return bo ? ~res + 1 : res;
}
typedef long long ll;
const int N = 405, ZZQ = 1e9 + 7;
int n, q, a[N], b[N], pre[N], nxt[N], stk[N], top, g[N][N], f[2][N][N],
sum[N][N], s[N][N];
bool is[N][N];
int Sum(int x)
{
return x * (x + 1) >> 1;
}
void jiejuediao(int x)
{
int i, j, k, L = pre[x] + 1, R = nxt[x] - 1;
For (i, L, R) is[i][a[x]] = 1;
For (i, L, R) For (j, L, R)
f[0][i][j] = f[1][i][j] = s[i][j] = 0;
f[0][L][R] = 1;
For (i, 1, q)
{
int op = i & 1; ll xsum;
For (j, L, R) For (k, j, R)
f[op][j][k] = 1ll * sum[j][k] * f[op ^ 1][j][k] % ZZQ;
For (k, L, R)
{
xsum = 0;
For (j, L, k)
{
f[op][j][k] = (xsum + f[op][j][k]) % ZZQ;
xsum = xsum + 1ll * f[op ^ 1][j][k] * (j - 1);
}
}
For (j, L, R)
{
xsum = 0;
Rof (k, R, j)
{
f[op][j][k] = (xsum + f[op][j][k]) % ZZQ;
xsum = xsum + 1ll * f[op ^ 1][j][k] * (n - k);
}
}
}
s[L][R] = f[q & 1][L][R];
Rof (i, R - 1, L) s[L][i] = (s[L][i + 1] + f[q & 1][L][i]) % ZZQ;
For (i, L + 1, R)
{
s[i][R] = (s[i - 1][R] + f[q & 1][i][R]) % ZZQ;
Rof (j, R - 1, L)
{
s[i][j] = ((s[i][j + 1] + s[i - 1][j]) % ZZQ
- s[i - 1][j + 1] + f[q & 1][i][j]) % ZZQ;
if (s[i][j] < 0) s[i][j] += ZZQ;
}
}
For (i, L, R) g[i][a[x]] = (g[i][a[x]] + s[i][i]) % ZZQ;
}
int main()
{
int i, j;
n = read(); q = read();
For (i, 1, n) a[i] = b[i] = read();
sort(b + 1, b + n + 1);
For (i, 1, n)
a[i] = lower_bound(b + 1, b + n + 1, a[i]) - b;
stk[top = 0] = 0;
For (i, 1, n)
{
while (top && a[stk[top]] < a[i]) top--;
pre[i] = stk[top];
stk[++top] = i;
}
stk[top = 0] = n + 1;
Rof (i, n, 1)
{
while (top && a[stk[top]] < a[i]) top--;
nxt[i] = stk[top];
stk[++top] = i;
}
For (i, 1, n) For (j, i, n)
sum[i][j] = Sum(i - 1) + Sum(n - j) + Sum(j - i + 1);
For (i, 1, n) jiejuediao(i);
For (i, 1, n)
{
int nx = 0;
For (j, 1, n)
{
if (!is[i][j]) continue;
int tmp = g[i][j];
g[i][j] = (g[i][j] - nx + ZZQ) % ZZQ;
nx = tmp;
}
}
For (i, 1, n)
{
int res = 0;
For (j, 1, n)
res = (res + 1ll * g[i][j] * b[j] % ZZQ) % ZZQ;
printf("%d ", res);
}
cout << endl;
return 0;
}