• JZOJ 6809. 【2020.10.29提高组模拟】不难题(容斥+DP)


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

    题目大意

    • K K K 1 − N 1-N 1N的排列,每次可以挑选一个队列取出队首,但不能连续取出 K K K个相同的数,要求取出每个区间 [ l , r ] [l,r] [l,r]中排列且不能连续取出 r − l + 1 r-l+1 rl+1个相同的数的方案数。
    • N , K ≤ 300 N,Kleq 300 N,K300

    题解

    • 这题可以联想到平面上只能向右向上走,要求到达某个点且有若干个点不能经过的方案数,
    • 可以用容斥来做设 f i f_i fi表示仅仅经过了第 i i i个不能经过的点的方案数,用总方案数减去其他 f j f_j fj即为 f i f_i fi,简单组合数计算即可。
    • 这题也是同理,但维度变成了 K K K维,同样设 f i f_i fi表示仅有 i i i连续出现了若干次,那么可以枚举 f j f_j fj转移到 f i f_i fi,当然要注意这里不只是组合数,而是先要到达每个 i i i前一个的位置,然后再乘阶乘,暴力做是 O ( N 2 K 3 ) O(N^2K^3) O(N2K3)的,中间计算简单优化一下可以达到 O ( N 2 K 2 ) O(N^2K^2) O(N2K2),仍旧是过不了。
    • 发现题目规定了是随机的,也就是区间跨度越大,可以转移的量就会越少,那么直接暴力把每次可以转移的记录下来,就可以过了。
    • 注意区间右端点最好要从左端点 + 1 +1 +1开始枚举,不然常数大可能过不了,另外可以尽量减少模运算的次数。

    代码

    #include<cstdio>
    #include<queue>
    using namespace std;
    #define N 310
    #define ll long long
    #define md 1000000007
    int a[N][N], p[N][N];
    ll F[N * N], G[N * N], f[N];
    int Sum[N][N], S[N][N], sum[N], s[N];
    queue<int> q[N];
    ll C(int x, int y) {
    	return F[x] * G[y] % md * G[x - y] % md;
    }
    ll ksm(ll x, ll y) {
    	if(!y) return 1;
    	ll l = ksm(x, y / 2);
    	if(y % 2) return l * l % md * x % md;
    	return l * l % md;
    }
    int main() {
    	int n, m, i, j, k, l, h, x;
    	scanf("%d%d", &m, &n);
    	F[0] = 1;
    	for(i = 1; i < N * N; i++) F[i] = F[i - 1] * i % md;
    	G[N * N - 1] = ksm(F[N * N - 1], md - 2);
    	for(i = N * N - 2; i >= 0; i--) G[i] = G[i + 1] * (i + 1) % md;
    	for(i = 1; i <= m; i++) {
    		for(j = 1; j <= n; j++) {
    			scanf("%d", &a[i][j]);
    			p[i][a[i][j]] = j;
    		}
    		a[i][n + 1] = p[i][n + 1] = n + 1;
    	}
    	ll ans = 0, tot;
    	for(i = 1; i < m; i++) {
    		for(k = 1; k <= n + 1; k++) sum[k] = p[i][k] - 1, s[k] = 1;
    		for(k = 1; k <= n + 1; k++)
    			for(l = 1; l < k; l++) if(p[i + 1][a[i][l]] < p[i + 1][a[i][k]]) q[a[i][k]].push(a[i][l]), Sum[a[i][k]][a[i][l]] = k - l - 1, S[a[i][k]][a[i][l]] = 1;
    		for(j = i + 1; j <= m; j++) {
    			for(h = 1; h <= n + 1; h++) {
    				k = a[i][h];
    				s[k] = s[k] * C(sum[k] + p[j][k] - 1, sum[k]) % md;
    				sum[k] += p[j][k] - 1;
    				f[k] = s[k] * F[j - i + 1] % md;
    				x = q[k].size(), tot = 0;
    				while(x--) {
    					l = q[k].front();
    					q[k].pop();
    					S[k][l] = S[k][l] * C(Sum[k][l] + p[j][k] - p[j][l] - 1, Sum[k][l]) % md;
    					Sum[k][l] += p[j][k] - p[j][l] - 1;
    					tot += f[l] * S[k][l] % md;
    					if(p[j + 1][l] < p[j + 1][k]) q[k].push(l);
    				}
    				f[k] = (f[k] - tot % md * F[j - i + 1] % md + md) % md;
    			}
    			ans += f[n + 1] * G[j - i + 1] % md;
    		}
    	}
    	printf("%lld
    ", ans % md);
    	return 0;
    }
    
    哈哈哈哈哈哈哈哈哈哈
  • 相关阅读:
    web中的安全编码
    网站安全(学习)
    head 命令(转)
    less 命令(转)
    简单网站优化
    Yahoo团队总结的关于网站性能优化的经验(转)
    more命令(转)
    linux安装oracle
    Ubuntu系统环境变量配置文件(转)
    nl命令(转)
  • 原文地址:https://www.cnblogs.com/LZA119/p/13910010.html
Copyright © 2020-2023  润新知