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 )

原文地址:https://www.cnblogs.com/yijan/p/1015comp.html