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}$

对这个求和就好了。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
#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)$

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
#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\n",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版本的

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
#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 ?
*/
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
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 )

文章作者: yijan
文章链接: https://yijan.co/old8/
版权声明: 本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来自 Yijan's Blog