洛谷P3352
BZOJ4574
UOJ#196
LOJ#2093
原本是一个期望 DP ,但是由于乘上了 ( n × ( n + 1 ) 2 ) q (\frac{n\times(n+1)}2)^q (2n×(n+1))q ,变成了计数 DP ,即求每个位置每种情况下的值之和。
数据是随机的,假设它们互不相同。先离散化。
很容易想到一个状态: g [ i ] [ j ] g[i][j] g[i][j] 表示第 i i i 个数变成从小到大第 j j j 个数的方案数。
那么 x x x 位置的答案为:
∑ i = 1 n g [ x ] [ i ] × b i \sum_{i=1}^ng[x][i]\times b_i i=1∑ng[x][i]×bi
其中 b i b_i bi 为从小到大第 i i i 个数的值。
考虑如何求 g [ i ] [ j ] g[i][j] g[i][j] 。如果有 a x = b j a_x=b_j ax=bj ,那么无论如何操作, 区间 [ l e x + 1 , r i x − 1 ] [le_x+1,ri_x-1] [lex+1,rix−1] 之外的值永远不会变成 a x a_x ax 。
其中 l e x le_x lex 为 x x x 左边第一个比 a x a_x ax 大的数的位置(不存在则为 0 0 0 )
r i x ri_x rix 为 x x x 右边第一个比 a x a_x ax 大的数的位置(不存在则为 n + 1 n+1 n+1 )
所以再定义状态 f [ i ] [ l ] [ r ] f[i][l][r] f[i][l][r] 表示第 i i i 轮之后恰好 区间 [ l , r ] [l,r] [l,r] 变成了 a x a_x ax 的方案数。
这样还是不好 (ke) 推。因为如果某次操作发生在 [ u , v ] [u,v] [u,v] ( u ≤ l e x < v u\le le_x<v u≤lex<v ),那么 l e x le_x lex 会增加或者 r i x ri_x rix 会减小(设分别改变至 L L L 和 R R R ),这样如果操作的左端点在 [ l e x + 1 , L ] [le_x+1,L] [lex+1,L] 而右端点大于等于 x x x 则修改对 a x a_x ax 的分布会特别难处理。
于是我们把状态中的 l l l 和 r r r 改为 l e x + 1 le_x+1 lex+1 和 r i x − 1 ri_x-1 rix−1 的值,即:
f [ i ] [ l ] [ r ] f[i][l][r] f[i][l][r] 表示第 i i i 轮之后恰好区间 [ l , r ] [l,r] [l,r] 小于等于 a x a_x ax 的方案数。
边界:
f [ 0 ] [ l e x + 1 ] [ r i x − 1 ] = 1 f[0][le_x+1][ri_x-1]=1 f[0][lex+1][rix−1]=1
转移(1):操作区间不包含 [ l , r ] [l,r] [l,r] :
f [ i ] [ l ] [ r ] + = ( l × ( l − 1 ) 2 + ( n − r ) ( n − r + 1 ) 2 + ( r − l + 1 ) ( r − l + 2 ) 2 ) f [ i − 1 ] [ l ] [ r ] f[i][l][r]+=(\frac{l\times(l-1)}2+\frac{(n-r)(n-r+1)}2+\frac{(r-l+1)(r-l+2)}2)f[i-1][l][r] f[i][l][r]+=(2l×(l−1)+2(n−r)(n−r+1)+2(r−l+1)(r−l+2))f[i−1][l][r]
其中 l × ( l − 1 ) 2 + ( n − r ) ( n − r + 1 ) 2 + ( r − l + 1 ) ( r − l + 2 ) 2 \frac{l\times(l-1)}2+\frac{(n-r)(n-r+1)}2+\frac{(r-l+1)(r-l+2)}2 2l×(l−1)+2(n−r)(n−r+1)+2(r−l+1)(r−l+2) 表示与区间 [ l , r ] [l,r] [l,r] 没有交集的区间个数。
转移(2):操作区间 [ u , l − 1 ] [u,l-1] [u,l−1] ( u < j u<j u<j )使得 ≤ a x \le a_x ≤ax 的区间 [ j , r ] [j,r] [j,r] 缩小到 [ l , r ] [l,r] [l,r] :
f [ i ] [ l ] [ r ] + = ∑ j = l e x + 1 l − 1 f [ i − 1 ] [ j ] [ r ] × ( j − 1 ) f[i][l][r]+=\sum_{j=le_x+1}^{l-1}f[i-1][j][r]\times(j-1) f[i][l][r]+=j=lex+1∑l−1f[i−1][j][r]×(j−1)
转移(3):操作区间 [ r + 1 , v ] [r+1,v] [r+1,v] ( v > j v>j v>j )使得 ≤ a x \le a_x ≤ax 的区间 [ l , j ] [l,j] [l,j] 缩小到 [ l , r ] [l,r] [l,r] :
f [ i ] [ l ] [ r ] + = ∑ j = r + 1 r i x − 1 f [ i − 1 ] [ l ] [ j ] × ( n − j ) f[i][l][r]+=\sum_{j=r+1}^{ri_x-1}f[i-1][l][j]\times(n-j) f[i][l][r]+=j=r+1∑rix−1f[i−1][l][j]×(n−j)
转移(2)(3)需要前缀和优化。
同样地重新设置 g [ i ] [ j ] g[i][j] g[i][j] 的定义: g [ i ] [ j ] g[i][j] g[i][j] 表示 q q q 轮后第 i i i 个数 ≤ a j \le a_j ≤aj 的方案数。
对于每个 a x a_x ax ( r k x rk_x rkx 为 a x a_x ax 的排名):
g [ i ] [ r k x ] + = ∑ j = l e x + 1 i ∑ k = i r i x − 1 f [ q ] [ j ] [ k ] g[i][rk_x]+=\sum_{j=le_x+1}^i\sum_{k=i}^{ri_x-1}f[q][j][k] g[i][rkx]+=j=lex+1∑ik=i∑rix−1f[q][j][k]
同样可以前缀和优化。
然后将 g g g 数组差分之后就得出 i i i 位置为 b j b_j bj 的方案数。
注意细节:如果对于一个位置 i i i 和一个 j j j ,如果有 a x = b j a_x=b_j ax=bj ,且 [ l e x + 1 , r i x − 1 ] [le_x+1,ri_x-1] [lex+1,rix−1] 不包含 i i i ,则 g [ i ] [ j ] g[i][j] g[i][j] 会为 0 0 0 ,而这样显然是不对的。所以,差分时应该枚举一个 i i i ,对于所有的满足 a x = b j a_x=b_j ax=bj 且 [ l e x + 1 , r i x − 1 ] [le_x+1,ri_x-1] [lex+1,rix−1] 包含 j j j 的 g [ i ] [ j ] g[i][j] g[i][j] 取出并差分。
复杂度 O ( 玄 学 ) O(玄学) O(玄学) 。
#include
#include
#include
#include
#include
#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;
}