[BZOJ4574][Zjoi2016]线段树(DP)

Address

洛谷P3352
BZOJ4574
UOJ#196
LOJ#2093

Solution

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

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;
}

猜你喜欢

转载自blog.csdn.net/xyz32768/article/details/82858706