zoukankan      html  css  js  c++  java
  • 10.15模拟赛

    10.15 模拟赛

    原题场。

    A 石子

    暑假,概率与期望ppt中的经典例题的一道题。。

    考虑令 $ x_i $ 表示第 $ i $ 堆石头在第几次被取。那么有

    $ x_1 = sum_i{ [x_i leq x_1] } = 1 + sum_i{[x_i < x_1]} $

    两边同时套一个期望,由期望的线性性

    $ E(x_i) = 1 + sum_iE([x_i < x_1]) = 1 + sum_i{P([x_i < x_1])} $

    然后求的就是第 $ i $ 堆石头比第一堆石头先取的概率。

    这个可以感性理解一下,我们要么先取第一堆石头要么先去第 $ i $ 堆石头,其他堆的石头貌似一点作用也没有,因为取了其他的石头不影响这两堆的先后顺序。所以其实概率就是只有两堆石头,随机取,取到 $ i $ 堆的概率,就是 $ frac{a_i}{a_1 + a_i} $

    对这个求和就好了。

    #include<iostream>
    #include<cstring>
    #include<algorithm>
    #include<cstdio>
    #include<vector>
    #include<queue>
    #include<stack>
    #include<bitset>
    #include<set>
    using namespace std;
    //#define int long long
    typedef long long ll;
    #define MAXN 200006 
    #define MAXM 450
    #define pb push_back
    #define pii pair<int,int>
    #define fi first
    #define se second
    #define mp make_pair
    #define inf 0x3f3f3f3f
    #define cmx( a , b ) a = max( a , b )
    #define cmn( a , b ) a = min( a , b )
    #define upd( a , b ) ( a = ( a + b ) % P )
    //#define P 1000000007
    int n;
    int a[MAXN];
    void read( signed& x ) {
    	scanf("%d",&x);
    }
    void read( ll& x ) {
    	scanf("%lld",&x);
    }
    long double res = 0.0;
    int main() {
    	read( n );
    	for( int i = 1 ; i <= n ; ++ i ) {
    		read( a[i] );
    		if( i != 1 ) res += ( long double ) a[i] / ( a[i] + a[1] );
    	}
    	res += 1.0;
    	printf("%.8Lf",res);
    }
    
    /*
     * Things you should pay attention to
     * inf is big enough?
     * out of bounds?
     * long long ?
     */
    

    B 内存

    首先,$ O( nmlog ) $ 的做法都会,就是枚举一下增加的数,然后二分判断就好了。

    但是如何优化这个东西呢?可以考虑给增加的数字 $ (1 , m-1) $ 随机打乱,每次如果无法使当前答案更优秀就不继续选了。

    为啥这样很优秀呢?我们假设对于新循环到的一个数 $ x $ ,新的这个数字比所有当前的数字都小的概率只有 $ frac{1}{i} $,所以最后期望只会跑 $ ln(n) $ 次二分check,总复杂度是 $ O(nm) $

    其实这个结论挺可推广的呢!

    #include<iostream>
    #include<cstring>
    #include<algorithm>
    #include<cstdio>
    #include<vector>
    #include<queue>
    #include<stack>
    #include<bitset>
    #include<set>
    using namespace std;
    //#define int long long
    typedef long long ll;
    #define MAXN 200006 
    #define MAXM 450
    #define pb push_back
    #define pii pair<int,int>
    #define fi first
    #define se second
    #define mp make_pair
    #define inf 0x3f3f3f3f
    #define cmx( a , b ) a = max( a , b )
    #define cmn( a , b ) a = min( a , b )
    #define upd( a , b ) ( a = ( a + b ) % P )
    //#define P 1000000007
    
    int n , m , k , blo;
    int A[MAXN] , S[MAXN] , smid[MAXN];
    void read( signed& x ) {
    	scanf("%d",&x);
    }
    void read( ll& x ) {
    	scanf("%lld",&x);
    }
    int a[MAXN];
    bool chk( int x ) {
    	int cur = 0 , kel = 0;
    	for( int i = 1 ; i <= n ; ++ i ) {
    		if( a[i] > x ) return false;
    		if( cur + a[i] > x ) cur = a[i] , ++ kel;
    		else cur += a[i];
    	}
    	if( cur ) ++ kel;
    	return kel <= k;
    }
    int P[MAXN];
    int main() {
    	cin >> n >> m >> k;
    	blo = log2( n );
    	for( int i = 1 ; i <= n ; ++ i ) read( A[i] ) , a[i] = A[i];
    	int l = 0 , r = 1e9;
    	while( l <= r ) {
    		int mid = l + r >> 1;
    		if( chk( mid ) ) r = mid - 1;
    		else l = mid + 1;
    	}
    	int cures = l;
    	for( int i = 1 ; i < m ; ++ i ) 
    		P[i] = i;
    	random_shuffle( P + 1 , P + m );
    	for( int i = 1 ; i < m ; ++ i ) {
    		int j = P[i];
    		for( int i = 1 ; i <= n ; ++ i ) a[i] = ( A[i] + j ) % m;
    		if( ! chk( cures - 1 ) ) continue;
    		int l = 0 , r = cures - 1;
    		while( l <= r ) {
    			int mid = l + r >> 1;
    			if( chk( mid ) ) r = mid - 1;
    			else l = mid + 1;
    		}
    		cures = l;
    	}
    	printf("%d
    ",cures);
    }
    
    /*
     * Things you should pay attention to
     * inf is big enough?
     * out of bounds?
     * long long ?
     */
    

    C 子集

    如果把 $ n $ 分解质因数,$ n = prod_i p_i^{c_i} $ ,对于每一个素数,必然有至少一个子集中的数不含这个因子,同时也必然有一个子集中的数字含有这个数字的 $ c_i $ 次方。然后就可以考虑容斥了!

    正常的容斥是所有情况减去不存在 0 次方的情况, 再去掉不存在 $ c_i $ 的情况,然后加上不存在的情况就好了。

    这样的情况数是 $ 4^k $ 的,$ k $ 最大是 16 左右,所以挂掉了。

    但是我们注意到 不存在0次方和不存在 $ c_i $ 次方的情况对答案的贡献是一样的!(都少了一个)所以可以 合并考虑。然后复杂度就是 $ 3^k $ 了

    怎么质因数分解呢?简单的做法是直接 $ Pollard-Rho $ ,但是我们也可以对 $ 10^6 $ 内的约数给拿出来,$ m $ 最多只有2个大于 $ 10^6 $ 的质数相乘,则 $ m = p , p^2 , pq $ 这三种形式。而且我们不关系分解后究竟是多少,我们只关心每个质数的系数,所以这样就可以了。

    死因:Miller-robbin 没龟速乘。。。。

    后面还有一个python版本的

    #include<iostream>
    #include<cstring>
    #include<algorithm>
    #include<cstdio>
    #include<vector>
    #include<queue>
    #include<stack>
    #include<bitset>
    #include<set>
    using namespace std;
    #define int long long
    typedef long long ll;
    #define MAXN 200006 
    #define MAXM 450
    #define pb push_back
    #define pii pair<int,int>
    #define fi first
    #define se second
    #define mp make_pair
    #define inf 0x3f3f3f3f
    #define cmx( a , b ) a = max( a , b )
    #define cmn( a , b ) a = min( a , b )
    #define upd( a , b ) ( a = ( a + b ) % P )
    #define P 998244353
    int n;
    void read( signed& x ) {
    	scanf("%d",&x);
    }
    void read( ll& x ) {
    	scanf("%lld",&x);
    }
    
    inline ll mul(ll a,ll b,ll p){
        a %= p; b %= p;
        if(p<=1e9)return 1ll*a*b%p;
        if(p<=1e12)return (((a*(b>>20)%p)<<20)%p+a*(b&(1<<20)-1))%p;
        ll d=1ll*floor(a*(long double)b/p);
    	ll res=(a*b-d*p)%p;if(res<0)res += p;
        return res;
    }
    
    int power(int x, int a , int p) {
    	int cur = x % P , ans = 1;
    	while( a ) {
    		if( a & 1 ) ans = mul( ans , cur , p );
    		cur = mul( cur , cur , p ) , a >>= 1;
    	}
    	return ans;
    }
    
    bool prime(int x) {
    	for(int i = 1; i <= 30; i++) {
    		int p = 1ll * rand() * rand() % x * rand() % x + 1;
    		while(p == x) p = 1ll * rand() * rand() % x * rand() % x + 1;
    		if(power(p, x - 1 , x) != 1) return false;
    	}
    	return true;
    }
    
    int val[MAXN], tp , ans = 0;
    void dfs(int now, int rev) {
    	if(now == tp + 1) {
    		int tmp = 1;
    		for( int i = 1; i <= tp; i++ ) tmp *= (val[i] + 1), tmp  %= P - 1;
    		ans = (ans + rev * (power(2, tmp , P) - 1) ) % P;
    		return;
    	}
    	dfs(now + 1, rev);
    	if(val[now] >= 1) -- val[now] , dfs(now + 1, (P - 2) * rev % P), ++ val[now];
    	if(val[now] >= 2) val[now] -= 2, dfs(now + 1, rev), val[now] += 2;
    }
    signed main() {
    	srand( 19260817 );
    	read( n );
    	for(int i = 2 ; i <= 1000000 && n > 1; i++) { // primes lower than 1e6
    		if( !( n % i ) ) ++ tp;
    		while( !( n % i ) ) val[tp]++ , n /= i;
    	}
    	if(n > 1) { 
    		if( prime(n) ) val[++tp] = 1; // n == prime * ppp
    		else if( (int)sqrt(n) * (int)sqrt(n) == n ) val[++tp] = 2; // n == prime * prime * ppp
    		else val[ ++tp ] = 1, val[ ++tp ] = 1; // elsewhise, val = 1
    	}
    	dfs(1, 1);
    	cout << ( ans + P ) % P ;
    }
    
    /*
     * Things you should pay attention to
     * inf is big enough?
     * out of bounds?
     * long long ?
     */
    
    import random
    import math
    
    n = 0
    P = 998244353
    
    def power( x , a , p ):
        cur = x % p
        ans = 1
        while( a ):
            if( a & 1 ):
                ans = ans * cur % p
            cur = cur * cur % p
            a >>= 1
        return ans
    
    def prime( x ):
        for i in range( 1 , 31 ): 
            p = random.randint( 1 , x - 1 )
            if( power( p , x - 1 , x ) != 1 ):
                return 0
        return 1
    
    val = []
    tp = 0
    ans = 0
    
    def dfs( now , rev ):
        global tp , ans , P
        if( now == tp + 1 ):
            tmp = 1
            for i in range( 1 , tp + 1 ):
                tmp = tmp * ( val[i] + 1 ) % ( P - 1 )
            ans = ( ans + rev * ( power( 2 , tmp , P ) - 1 ) ) % P
            return
        dfs( now + 1 , rev )
        if( val[now] >= 1 ):
            val[now] = val[now] - 1
            dfs( now + 1 , ( P - 2 ) * rev % P )
            val[now] = val[now] + 1
        if( val[now] >= 2 ):
            val[now] = val[now] - 2
            dfs( now + 1 , rev )
            val[now] = val[now] + 2
        return
    
    n = int(input())
    val.append( 0 )
    for i in range( 2 , 1000001 ):
        if( n <= 1 ):
            break
        if( n % i == 0 ):
            tp = tp + 1
            val.append( 0 )
        while( n % i == 0 ):
            val[tp] = val[tp] + 1
            n //= i
    if( n > 1 ):
        if( prime( n ) == 1 ):
            tp = tp + 1 
            val.append( 1 )
        else:
            if( int(math.sqrt( n )) * int( math.sqrt( n ) ) == n ):
                tp = tp + 1
                val.append( 2 )
            else:
                tp = tp + 1
                val.append( 1 )
                tp = tp + 1
                val.append( 1 )
    dfs( 1 , 1 )
    print( ( ans + P ) % P )
    
    
  • 相关阅读:
    jdk源码剖析三:锁Synchronized
    ASP.NET的session操作方法总结
    C#文件相同性判断
    C#的DataTable操作方法
    C#二进制流的序列化和反序列化
    C#常用的IO操作方法
    C#缓存操作
    CLR中的程序集加载
    Oracle数据库的SQL分页模板
    奇妙的NULL值,你知道多少
  • 原文地址:https://www.cnblogs.com/yijan/p/1015comp.html
Copyright © 2011-2022 走看看