[training question 47: probability dp | dynamic programming | optimization] Increasing Subsequence | 2021 Niuke summer multi school training camp 1 question I

Posted by Rowno on Tue, 04 Jan 2022 03:00:12 +0100

meaning of the title

  • Increasing subsequence 2021 Niuke summer multi school training camp 1
    Count Reg n n Array of n, No i i i number is a i a_i ai, and this array is a [ 1 , n ] [1,n] Full Permutation of [1,n]
    A l i c e Alice Alice selects a location at random with equal probability x x x. Then B o b Bob Bob also randomly selects a location with equal probability y y y. But request a y > a x a_y>a_x ay​>ax​
    then A l i c e Alice Alice first, take turns with two people:
  • Suppose ta the subscript selected in the previous step is x x x. The subscript selected by the other party in the previous step is y y y
    Then ta must select a location k k k. Satisfied k > x k > x k> X and a k > a y a_k>a_y ak​>ay​
    If there are multiple satisfied locations, ta will randomly select one with equal probability
    If there is no satisfied position, it ends
    Ask you what you expect from the beginning to the end
  • 1 ≤ n ≤ 5000 1\le n\le 5000 1≤n≤5000

thinking

  • When you see the expectation and the data range, you will certainly think of an expectation d p dp dp
    set up d p [ i ] [ j ] dp[i][j] dp[i][j] indicates A l i c e Alice Alice took the subscript i i i , B o b Bob Bob took the subscript j j j. Expectation of the round to the end
    Our basis a i , a j a_i,a_j The size relationship between ai and aj , you can judge who will take the number next
    hypothesis a i > a j a_i>a_j ai > AJ, then B o b Bob Bob, we can write the transfer ourselves:
    d p [ i ] [ j ] = 1 c ∑ k > j a k > a i d p [ i ] [ k ] + 1 dp[i][j]=\frac{1}{c}\sum_{\underset{a_k>a_i}{k>j}}dp[i][k] + 1 dp[i][j]=c1​ak​>ai​k>j​∑​dp[i][k]+1
    hypothesis a i < a j a_i<a_j ai < AJ, then A l i c e Alice Alice:
    d p [ i ] [ j ] = 1 c ∑ k > i a k > a j d p [ k ] [ j ] + 1 dp[i][j]=\frac{1}{c}\sum_{\underset{a_k>a_j}{k>i}}dp[k][j] + 1 dp[i][j]=c1​ak​>aj​k>i​∑​dp[k][j]+1
    here c c c is how many k k k meet the above requirements
    But doing it directly is, of course O ( N 3 ) O(N^3) O(N3), direct T L E \color{red}{TLE} TLE
    I used to drive 4 n 4n 4n tree arrays to do, but O ( N 2 log ⁡ N × c ) O(N^2\log N\times c) O(N2logN × c) Also T L E \color{red}{TLE} TLE
    In fact, we can optimize to O ( N 2 ) \color{cyan}{O(N^2)} O(N2)
  • Both state dimensions are necessary, that is, we can only go O ( 1 ) O(1) O(1) obtains the following summation
    Because we must write code like this:
	for(int i = n;i >= 1;--i)
	for(int j = n;j >= 1;--j){
		dp[i][j] = Something
	}
  • about B o b Bob Bob, we need to find out quickly ∑ k > j a k > a i d p [ i ] [ k ] \sum_{\underset{a_k>a_i}{k>j}}dp[i][k] ∑ak​>ai​k>j​​dp[i][k]
    Because we are j j j from n n n enumerates up to the present in reverse order, so we just need to i i i. How many records are satisfied a k > a i a_k>a_i And put their ak, AI > d p [ i ] [ k ] dp[i][k] dp[i][k] and record it O ( 1 ) O(1) O(1) got it
    That's about it. It's easy to understand:
	for(int i = n;i >= 1;--i){
        ll c = 0;
        ll sum1 = 0;
        for(int j = n;j >= 1;--j){
            if(i == j)continue;
            if(aa[i] > aa[j]){
                dp[i][j] = (1 + iv[c] * sum1) % MOD;		// iv[c] = 1 / c
            }else{
                c++;
                sum1 = (sum1 + dp[i][j]) % MOD;
            }
        }
    }
  • about A l i c e Alice Alice, we need to find out quickly ∑ k > i a k > a j d p [ k ] [ j ] \sum_{\underset{a_k>a_j}{k>i}}dp[k][j] ∑ak​>aj​k>i​​dp[k][j]
    Because of our i i i also from n n n enumerating in reverse order until now, it is satisfied by default k > i k>i k>i
    For all a k > a j a_k>a_j In the case of ak > AJ , we need to record how many times we are satisfied, and d p [ k ] [ j ] dp[k][j] Sum of dp[k][j]
    here j j j has many values, so let's just open a number c n t [ j ] , s u m [ j ] cnt[j],sum[j] cnt[j] and sum[j] store the corresponding content
    for(int i = n;i >= 1;--i){
        ll c = 0;
        ll sum1 = 0;
        for(int j = n;j >= 1;--j){
            if(i == j)continue;
            if(aa[i] > aa[j]){
                dp[i][j] = (1 + iv[c] * sum1) % MOD;
                cnt[j]++;
                sum[j] = (sum[j] + dp[i][j]) % MOD;
            }else{
                dp[i][j] = (1 + iv[cnt[j]] * sum[j]) % MOD;
                c++;
                sum1 = (sum1 + dp[i][j]) % MOD;
            }
        }
    }
  • A little more modification, because at the beginning, it was not chosen by two people at once, but first A l i c e Alice Alice chooses again B o b Bob Bob's choice
    Then we B o b Bob Bob's subscript is selected to 0 0 0 means B o b Bob Bob hasn't started choosing yet
    for(int i = n;i >= 1;--i){
        ll c = 0;
        ll sum1 = 0;
        for(int j = n;j >= 0;--j){
            if(i == j)continue;
            if(aa[i] > aa[j]){
                dp[i][j] = (1 + iv[c] * sum1) % MOD;
                cnt[j]++;
                sum[j] = (sum[j] + dp[i][j]) % MOD;
            }else{
                dp[i][j] = (1 + iv[cnt[j]] * sum[j]) % MOD;
                c++;
                sum1 = (sum1 + dp[i][j]) % MOD;
            }
        }
    }
  • What is the final answer? Is the expectation of all opening situations divided by the number of opening situations
	for(int i = 1;i <= n;++i){
        ans = (ans + dp[i][0]) % MOD;
    }
    ans = ans * iv[n] % MOD;

code

  • Time complexity: O ( N 2 ) O(N^2) O(N2)
#include <bits/stdc++.h>
#define IOS ios::sync_with_stdio(false);cin.tie(NULL);cout.tie(NULL);
using namespace std;
typedef long long ll;
void show(){std::cerr << endl;}template<typename T,typename... Args>void show(T x,Args... args){std::cerr << "[ " << x <<  " ] , ";show(args...);}

const int MAX = 5e3+50;
const int MOD = 998244353;
const int INF = 0x3f3f3f3f;
const ll LINF = 0x3f3f3f3f3f3f3f3f;
const double EPS = 1e-5;


ll qpow(ll a,ll n){/* */ll res = 1LL;while(n){if(n&1)res=res*a%MOD;a=a*a%MOD;n>>=1;}return res;}
ll qpow(ll a,ll n,ll p){a%=p;ll res = 1LL;while(n){if(n&1)res=res*a%p;a=a*a%p;n>>=1;}return res;}
ll npow(ll a,ll n){/* */ll res = 1LL;while(n){if(n&1)res=res*a;a=a*a;n>>=1;if(res<0||a<0)return 0;}return res;}
ll inv(ll a){/* */return qpow(a,MOD-2);}
ll inv(ll a,ll p){return qpow(a,p-2,p);}

int aa[MAX];
ll dp[MAX][MAX];
ll iv[MAX];
ll sum[MAX],cnt[MAX];
int main()
{
    IOS;
    int n;cin >> n;
    for(int i = 1;i <= n;++i)cin >> aa[i],iv[i] = inv(i);
    iv[0] = 1;
    for(int i = n;i >= 1;--i){
        ll c = 0;
        ll sum1 = 0;
        for(int j = n;j >= 0;--j){
            if(i == j)continue;
            if(aa[i] > aa[j]){
                dp[i][j] = (1 + iv[c] * sum1) % MOD;
                cnt[j]++;
                sum[j] = (sum[j] + dp[i][j]) % MOD;
            }else{
                dp[i][j] = (1 + iv[cnt[j]] * sum[j]) % MOD;
                c++;
                sum1 = (sum1 + dp[i][j]) % MOD;
            }
        }
    }
    ll ans = 0;
    for(int i = 1;i <= n;++i){
        ans = (ans + dp[i][0]) % MOD;
    }
    ans = ans * iv[n] % MOD;
    cout << ans;
    return 0;
}
/**

*/

Topics: Algorithm Dynamic Programming