6809. 【2020.10.29提高组模拟】不难题

大意:给你\(K\)\(1\)\(n\)的排列,让你求\([L,R]\)的序列的操作方案数。
限定条件:每次操作只能取那些序列中的队首,且取出来组成的序列中不存在连续\(R-L+1\)个连续的相同的数。
\(n,K<=300\)

\(Solution\)

这题可想到平面上只能向右向下走,有若干个障碍,从\((1,1)\)走到\((n,m)\)的方案数。
考虑容斥,设\(f[i]\)表示当前\([l,r]\)区间中取出来的操作中第一次出现连续的\(r-l+1\)个连续相同数为\(i\),且考虑\(i\)的取出顺序而不考虑\(i\)以后的那些数的取出顺序的方案数。
\(f[i]\)可以由取\(i\)以前的数的总方案数减去前面更早出现了连续\(r-l+1\)个连续相同数的方案数。
简而言之,若\([l,r]\)区间内存在\(j\)使得在其中每个序列内\(j\)的出现位置都比\(i\)要前,则\(f[i]\)需减去\(f[j]*h(j,i)\)
其中\(h(j,i)\)表示\(j\)\(i\)之间的那些数的取出方案数。
对于容斥,我们对\(i\)在新的(第\(r\)个)序列中的位置从小到大来进行操作,这样可以保证以前的所有\(f[j]\)都是包含新的(第\(r\)个)序列的第一次出现的连续的\(r-l+1\)个连续相同的数的方案数。
这样我们就不会算重或算漏了。
为了方便,我们在每个序列后面添加一个\(n+1\),这样对于每个\([l,r]\)区间的答案就应当是\(f[n+1]*ny[r-l+1]\)了(由于答案不需要考虑\(n+1\)的取出顺序)。
则最终的答案就是\(\sum f[n+1]\)
上述容斥的时间复杂度为\(O(K^2*K*n^2)\),显然会\(TLE\)
(也可用维护那些组合数来达到\(O(K^2*n^2)\)的效果,但\(TLE\)在所难免)

既然没有天时和人和,我们尝试地利(↓):

既然是说了独立的随机的排列。那对于\([L,R]\)区间中仍能保证一对\((j,i)\)\(j\)总是在\(i\)前面的是少之又少,期望是每次个数都\(div\ 2\)
这样我们可以直接存储下来对于每个\(x\)当前仍符合条件的\(y\),然后每次操作后改删的删除即可。
时间复杂度大约为\(O(n*K*(n+K))\)
但是,很容易\(TLE\),第一发\(TLE90\)了。
尝试\(AC\)中。。。代码待更。。。
\(AC\)(果然还是\(int\)\(1LL\)大法好)

\(Code\)

#include <cstdio>
#define N 310
#define mo 1000000007
#define ll long long
#define fo(x, a, b) for (int x = (a); x <= (b); x++)
#define fd(x, a, b) for (int x = (a); x >= (b); x--)
using namespace std;
int K, n, a[N][N], b[N][N], hav;
int qian_[N], qn[N][N], pl[N][N], cnt[N], f[N];
int value[N], val[N][N], jc[N * N], ny[N * N], ans = 0;
	
inline int read() {
	int x = 0, f = 0; char c = getchar();
	while (c < '0' || c > '9') f = (c == '-') ? 1 : f, c = getchar();
	while (c >= '0' && c <= '9') x = x * 10 + c - '0', c = getchar();
	return f ? -x : x;
}

int ksm(int x, int y) {
	int s = 1;
	while (y) {
		if (y & 1) s = 1LL * s * x % mo;
		x = 1LL * x * x % mo, y >>= 1;
	}
	return s;
}

void prepare() {
	jc[0] = ny[0] = 1;
	fo(i, 1, 90000) jc[i] = 1LL * jc[i - 1] * i % mo;
	ny[90000] = ksm(jc[90000], mo - 2);
	fd(i, 89999, 1) ny[i] = 1LL * ny[i + 1] * (i + 1) % mo;
}

inline ll C(int x, int y) {return 1LL * jc[y] * ny[x] % mo * ny[y - x] % mo;}

int main()
{
	freopen("nothard.in", "r", stdin);
	freopen("nothard.out", "w", stdout);
	prepare();
	K = read(), n = read(); 
	fo(i, 1, K) {
		fo(j, 1, n) a[i][j] = read(), pl[i][a[i][j]] = j;
		a[i][n + 1] = n + 1, pl[i][n + 1] = n + 1;
	}
	n++;
	fo(L, 1, K - 1) {
		fo(i, 1, n) f[i] = cnt[i] = 0; f[a[L][1]] = 1;
		fo(i, 1, n) value[a[L][i]] = 1, qian_[a[L][i]] = i - 1;
		fo(j, 2, n) fo(i, 1, j - 1) {
			int x = a[L][j]; b[x][++cnt[x]] = a[L][i];
			qn[x][cnt[x]] = j - i - 1, val[x][cnt[x]] = 1;
		}
		fo(R, L + 1, K) {
			fo(i, 1, n) {
				int x = a[R][i]; qian_[x] += i - 1;
				value[x] = 1LL * value[x] * C(i - 1, qian_[x]) % mo;
				f[x] = value[x];
			}
			fo(i, 1, n) {
				int x = a[R][i];
				fo(j, 1, cnt[x]) {
					if (i < pl[R][b[x][j]]) {
						b[x][j] = b[x][cnt[x]], qn[x][j] = qn[x][cnt[x]];
						val[x][j] = val[x][cnt[x]], cnt[x]--; j--; continue;
					}
//					printf("%d %d\n", pl[R][b[x][j]], i);
					int t = i - pl[R][b[x][j]] - 1;
					val[x][j] = 1LL *val[x][j] * C(t, qn[x][j] + t) % mo; qn[x][j] += t;
					f[x] = f[x] + mo - 1LL * val[x][j] * f[b[x][j]] % mo;
					if (f[x] >= mo) f[x] -= mo;
				}
				f[x] = 1LL * f[x] * jc[R - L + 1] % mo;
			}
//			printf("%d %d: %lld\n", L, R, f[n]);
			ans = (ans + 1LL * f[n] * ny[R - L + 1]) % mo;
		}
	}
	printf("%d\n", ans);
	return 0;
}
posted @ 2020-11-02 09:39  jz929  阅读(88)  评论(0编辑  收藏  举报