zoukankan      html  css  js  c++  java
  • CF1151FSonya and Informatics

    CF1151FSonya and Informatics

    给一个长度为 n$ (nleq 100)$的 (0/1) 串,进行 k((k leq 10^9))次操作,每次操作选择两个位置 ((i,j))((i < j)),交换$ i,j$ 上的数,求 (k) 次操作后,该 (0/1) 串变成非降序列的概率,答案对 (10^9+7) 取模。

    首先这道题目我们可以想出来一下比较朴素的DP

    我们设(sum_0)为原列中(0)个数

    我们发现我们不关心序列长什么样子

    只关心前(sum_0)个数中(0)个个数

    (f_{i,j})表示前(i)次操作之后,还有(j)(0)的方案数

    概率可以转化为

    [frac{f_{k,sum_0}}{sum_{i = 0}^{sum_0} f_{k,i}} ]

    转移就是

    (c = frac{n*(n - 1)}{2})表示总的可能情况

    [f_{i,j} += f_{i - 1,j - 1}+(sum_0 - (j - 1)) * (sum_0 - (j - 1)) ]

    把一个前面的(1)换成(0)前部分有(sum_0- (j - 1))(1),后一部分有(sum_0 - (j - 1))(0),两两配对的方案数

    [f_{i,j} += f_{i - 1,j + 1} + (j + 1) * (sum_1 - (sum_0 - (j + 1))) ]

    把一个前面的(0)换成(1),组合意义类似上面

    [f_{i,j} += f_{i - 1,j} + (c - v_1 - v_2) ]

    (v_1,v_2)就是上面两个式子的后面部分

    表示没有对前面的(0)的数目产生影响

    这之后我们观察一下这个转移每次转移只和(j)有关

    我们考虑使用矩阵去优化这个东西

    [left[egin{array}{ccccc}{f_{0}[0]} & {f_{-1}[1]} & {0} & {dots} & {0} \ {f_{+1}[0]} & {f_{0}[1]} & {f_{-1}[2]} & {dots} & {0} \ {0} & {f_{+1}[1]} & {f_{0}[2]} & {dots} & {0} \ {dots} & {dots} & {dots} & {dots} & {dots}\{0}&{0}&{0}&{dots} &{f_0[n]}end{array} ight] left[egin{array}{c}{d p[0]} \ {d p[1]} \ {d p[2]} \ {cdots} \ {d p[n]}end{array} ight]=left[egin{array}{c}{d p[0]} \ {d p[1]} \ {d p[2]} \ {cdots} \ {d p[n]}end{array} ight] ]

    大约是这个样子

    我们这样就完成了一次转移

    (k)很大

    我们直接上矩阵快速幂就可以了

    (mathcal{O}left(n^{3} log k ight))

    #include<cstdio>
    #include<iostream>
    #include<queue>
    #include<algorithm>
    #include<cstring>
    #include<cctype>
    #include<vector>
    #include<ctime>
    #include<cmath>
    #define LL long long
    #define pii pair<int,int>
    #define mk make_pair
    #define fi first
    #define se second
    using namespace std;
    const int N = 105;
    const LL mod = 1e9 + 7;
    int n,k; 
    int a[N];
    LL sum0,sum1;
    inline int read(){
    	int v = 0,c = 1;char ch = getchar();
    	while(!isdigit(ch)){
    		if(ch == '-') c = -1;
    		ch = getchar();
    	}
    	while(isdigit(ch)){
    		v = v * 10 + ch - 48;
    		ch = getchar();
    	}
    	return v * c;
    }
    inline LL up(LL x){
    	if(x >= mod) x -= mod;
    	return x;	
    }
    inline LL down(LL x){
    	if(x < 0) x += mod;
    	return x;	
    }
    struct Ma{
    	LL a[N][N];	
    	LL	*operator[](int i){return a[i];}
    	inline void clear(){memset(a,0,sizeof(a));}	
    	friend Ma operator * (Ma x,Ma y){
    		Ma c;c.clear();
    		for(int i = 0;i <= n;++i)
    			for(int j = 0;j <= n;++j)
    				for(int k = 0;k <= n;++k)
    					c.a[i][j] = (c.a[i][j] + (x.a[i][k] * y.a[k][j])) % mod;
    		return c;
    	}
    };
    Ma s,ss;
    inline Ma quick(Ma x,int y){
    	Ma res;
    	for(int i = 0;i <= n;++i) res[i][i] = 1;
    	while(y){
    		if(y & 1) res = res * x;
    		y >>= 1;
    		x = x * x;	
    	}
    	return res;
    }
    Ma dp;
    LL inv;
    inline LL p3(LL x){
    	return x * (sum0 - (sum1 - x)) % mod;
    }
    inline LL p2(LL x){
    	LL v1 = sum1 * (sum1 - 1) / 2;
    	LL v2 = sum0 * (sum0 - 1) / 2;
    	LL v3 = x * (sum1 - x) % mod;
    	LL v4 = (sum1 - x) * (sum0 - (sum1 - x)) % mod;
    	return (v1 + v2 + v3 + v4) % mod;
    }
    inline LL p1(LL x){
    	return (sum1 - x) * (sum1 - x) % mod;
    }
    inline LL quickmul(LL x,LL y){
    	LL res = 1;
    	while(y){
    		if(y & 1) res = res * x % mod;
    		x = x * x % mod;
    		y >>= 1;
    	}
    	return res;
    }
    int main(){
    	n = read(),k = read();	
    	for(int i = 1;i <= n;++i){
    		a[i] = read() ^ 1;	
    		sum0 += (a[i] == 0);
    		sum1 += (a[i] == 1);
    	}
    	LL t = 0;
    	for(int i = 1;i <= sum1;++i) t += (a[i] == 1);
    	dp[t][0] = 1;
    	for(int i = 0;i <= sum1;++i){
    		if(i > 0) ss.a[i][i - 1] = p1(i - 1);
    		ss.a[i][i] = p2(i);
    		if(i + 1 <= sum1) ss.a[i][i + 1] = p3(i + 1);	
    	}
    	Ma ans = quick(ss,k) * dp;
    	Ma aa = quick(ss,k);
    	LL res = 0;
    	for(int i = 0;i <= sum1;++i) res = (res + ans[i][0]) % mod;
    	printf("%lld
    ",ans[sum1][0] * quickmul(res,mod - 2) % mod);
    	return 0;
    }
    
    
  • 相关阅读:
    容斥原理
    m元集A到n元集B的满射的个数
    二项式反演公式
    多项式定理
    组合数的基本性质
    Luogu P2408 不同子串个数
    Luogu P5410【模板】扩展 KMP
    Luogu P2336 [SCOI2012]喵星球上的点名
    Luogu P2852 [USACO06DEC]牛奶模式Milk Patterns
    Luogu P4248 [AHOI2013]差异
  • 原文地址:https://www.cnblogs.com/wyxdrqc/p/11408807.html
Copyright © 2011-2022 走看看