• [牛客多校]第一场 I. Increasing Subsequence


    题意: 给定一个(1~n)的排列,两个人分别选数,每个人这次选的数必须比自己上次选的位置要靠后,同时必须比两人之前选的所有数都要大,如果有多个满足条件的数,他们就会等概率选取一个。最开始的那个人可以等概率选取任何一个数。求最终选取的数的期望个数。
    范围: (nle 5000)
    题解: 有两个限制条件。第一,自己选的位置递增,第二,选的数字比之前所有数字都要大。

    比较naive的想法是首先干掉第一个条件,于是设计出dp状态:(f[i][j][0/1])表示第一个人上一次选了i位置的数,第二个人选了j位置的数,这一次轮到哪个人选的概率。
    一个(我自己)比较想不到的点是:一个局面变换的代价是(1)(走一步),那么每个局面对答案的贡献是它自己的概率乘(1)
    枚举递增的位置,并且满足数值递增进行转移,这个枚举不太好节省,时间是(O(n^3))(见code1)。

    那么,如何设计一个更加好的dp方案呢?
    注意到DP本质是在一个DAG上往后走的过程,我们可以定义出一个势能,而所有DP都是往势能降低的方向进行的。
    在这道题上,我们发现,有两个势能可以选择:

    1. 每个人的位置,就是上面那个DP方法,但是这个势能是在一个二维平面上单减的,有两个维度要考虑,所以十分复杂。
    2. 选取的最大数字,这个就十分的amazing啊,你可以发现,这个数字是一维的,也就是说,假如DP顺序按照这个数字递减,就可以设计出一个比较优秀的方案了。

    不过这种方法也有个局限性,当数字并不是两两不同,并且选取不一定要单调的时候,就无法进行dp,因为这时候势能不是随着这个数字递减的(可以选相同数字),这时候只能通过升维进行DP了。

    我们可以让(f[i][j])表示上个人选了(i),上上个人选了(j)
    这时候的势能为(i),并且满足(i<j)
    我们往势能降低的方向进行dp,我们可以从(f[i][j])转移到(f[k][i](k<i))
    转移的时候,我们需要知道有(i+1~n)多少个数在(k)的后面。
    记一个数的位置为(p[x]),有(cnt)个数。

    [f[k][i] = sum_{j = i + 1} ^ n [p[j] > p[k]] imes f[i][j] / cnt ]

    后面这个式子只有([p[j] > p[k]], cnt)(k)是有关的,这个相关性是“所有在(k)后面的数”,就是一个前缀和,所以我们对每个大于(i)的值,按照位置维护一个前缀和,就可以快速搞出这个值了。

    最终势能降为0的位置就是答案,即(sum_{i = 1} ^ {n} f[0][i]),见code2。
    当然最开始(n)个数任意选,所以答案还要除一个(n)

    code1

    #include <bits/stdc++.h>
    #define pt(x) cout << x << endl;
    #define Mid ((l + r) / 2)
    #define lson (rt << 1)
    #define rson (rt << 1 | 1)
    using namespace std;
    int read() {
    	char c; int num, f = 1;
    	while(c = getchar(),!isdigit(c)) if(c == '-') f = -1; num = c - '0';
    	while(c = getchar(), isdigit(c)) num = num * 10 + c - '0';
    	return f * num;
    }
    const int mod = 998244353;
    const int N = 5009;
    int n, a[N], f[N][N][2], p[N][N][2], inv[N];
    int sum[N];
    int tot[N][N];
    signed main()
    {
    	memset(p, 0, sizeof(p));
    	inv[0] = inv[1] = 1;
    	for(int i = 2; i < N; i++) inv[i] = (1ll * mod - mod / i) * inv[mod % i] % mod;
    	n = read();
    	for(int i = 1; i <= n; i++) a[i] = read();
    	for(int i = 1; i <= n; i++) 
    		for(int j = n; j >= 1; j--) 
    			tot[i][j] = tot[i][j + 1] + (a[j] > a[i]);
    	for(int i = 1; i <= n; i++) p[i][0][0] = inv[n];
    	for(int i = 1; i <= n; i++) {
    		int cnt = 0;
    		memset(sum, 0, sizeof(sum));
    		for(int j = 1; j <= n; j++) {
    			
    		}
    		for(int j = 1; j <= n; j++) {
    			for(int k = 0; k < j; k++) if(a[j] > max(a[k], a[i])){
    				int cnt = 0;
    				if(a[k] > a[i]) cnt = tot[k][k + 1];
    				else cnt = tot[i][k + 1];
    				p[i][j][1] = (p[i][j][1] + 1ll * p[i][k][0] * inv[cnt] % mod) % mod;
    			}
    			for(int k = 0; k < i; k++) if(a[i] > max(a[k], a[j])){
    				int cnt = 0;
    				if(a[k] > a[j]) cnt = tot[k][k + 1];
    				else cnt = tot[j][k + 1];
    				p[i][j][0] = (p[i][j][0] + 1ll * p[k][j][1] * inv[cnt] % mod) % mod;
    			}
    		}
    	}
    	int ans = 0;
    	for(int i = 0; i <= n; i++) {
    		for(int j = 0; j <= n; j++) {
    			if(p[i][j][1] != -1) ans = (ans + p[i][j][1]) % mod;
    			if(p[i][j][0] != -1) ans = (ans + p[i][j][0]) % mod;
    		}
    	}
    	printf("%d
    ", ans);
    	return 0;
    }
    

    code2

    #include <bits/stdc++.h>
    #define pt(x) cout << x << endl;
    #define Mid ((l + r) / 2)
    #define lson (rt << 1)
    #define rson (rt << 1 | 1)
    using namespace std;
    int read() {
    	char c; int num, f = 1;
    	while(c = getchar(),!isdigit(c)) if(c == '-') f = -1; num = c - '0';
    	while(c = getchar(), isdigit(c)) num = num * 10 + c - '0';
    	return f * num;
    }
    const int mod = 998244353;
    const int N = 5009;
    int n, a[N], f[N][N], p[N], inv[N];
    int sum[N], cnt[N];
    int tot[N][N];
    signed main()
    {
    	memset(p, 0, sizeof(p));
    	inv[0] = inv[1] = 1;
    	for(int i = 2; i < N; i++) inv[i] = (1ll * mod - mod / i) * inv[mod % i] % mod;
    	n = read();
    	for(int i = 1; i <= n; i++) p[read()] = i;
    	for(int i = n - 1; ~i; i--) {
    		memset(cnt, 0, sizeof(int) * (n + 5));
    		memset(sum, 0, sizeof(int) * (n + 5));
    		for(int j = i + 1; j <= n; j++) {
    			cnt[p[j]]++;
    			sum[p[j]] = (sum[p[j]] + f[i][j]) % mod;
    		}
    		for(int j = n - 1; ~j; j--) cnt[j] += cnt[j + 1], sum[j] = (sum[j] + sum[j + 1]) % mod;
    		for(int j = 0; j != i; j++) {
    			if(cnt[p[j]])
    				f[j][i] = (1ll 	* sum[p[j]] * inv[cnt[p[j]]] % mod + 1) % mod;
    		}
    	}
    	int ans = 0;
    	for(int i = 1; i <= n; i++) ans = (ans + f[0][i]) % mod;
    	printf("%lld
    ", (1ll * ans * inv[n] + 1) % mod);
    	return 0;
    }
    
  • 相关阅读:
    find
    fdisk
    falcon-eye
    ethtools
    e2fsck
    dpkg
    declare
    df
    debconf-utils
    区块链从零开始做开发(0):hyperledger Fabric2.3安装
  • 原文地址:https://www.cnblogs.com/onglublog/p/15303140.html
Copyright © 2020-2023  润新知