2021.11.06模拟赛

T1 Bogo

  终于有一道我会做的期望题了,太感动了。随机排序到升序的概率就是:
p = ∏ i = 1 k c i n ! p = \frac{\prod\limits_{i=1}^kc_i}{n!} p=n!i=1kci

  其中 k k k 是重复的数的组数, c i c_i ci 是每组重复的数的个数。然后是期望,我们设期望是 E,那么就有:
E = p + ( 1 − p ) ( E + 1 ) = p + E + 1 − p E − p → E = 1 p E = p + (1-p)(E + 1) = p + E + 1 - pE - p \rightarrow E = \frac 1p E=p+(1p)(E+1)=p+E+1pEpE=p1

  于是:
E = n ! ∏ i = 1 k c i E = \frac{n!}{\prod\limits_{i=1}^kc_i} E=i=1kcin!

  对于上面那个玩意儿,显然是 O ( n ) O(n) O(n) 处理掉,然后下面那个东西可以先对数组排个序然后就可以 O ( n ) O(n) O(n) 处理了。总的复杂度 O ( n + n log ⁡ n ) O(n +n \log n) O(n+nlogn)。稳稳的过 1 e 5 1e5 1e5

  还有一个需要注意的地方,就是如果数列一开始就是升序的,那么我们就要直接输出 0(我因为没注意到这个痛失十分)。

#include<bits/stdc++.h>
using namespace std;
#define MOD (int)(1e9 + 7)
#define MAXN 100100
#define MAXI (int)1e8
#define in read()

inline int read(){
	int x = 0; char c = getchar();
	while(c < '0' or c > '9') c = getchar();
	while('0' <= c and c <= '9'){
		x = x * 10 + c - '0'; c = getchar();
	}
	return x;
}

int n = 0;
int a[MAXN] = { 0 };
int inv[MAXI] = { 0 };

void prework(){
	inv[1] = 1;
	for(int i = 2; i <= n; i++){
		inv[i] = (1ll * MOD - MOD / i) * inv[MOD % i] % MOD;
	}
}

int main(){
	n = in; prework();
	for(int i = 1; i <= n; i++) a[i] = in;
	bool is = true;
	for(int i = 1; i < n; i++)
		if(a[i] > a[i + 1]){
			is = false; break;
		}
	if(is){
		printf("0\n"); return 0;
	}
	sort(a + 1, a + n + 1);
	int ans = 1;
	for(int i = 1; i <= n; i++) ans = (1ll * ans * i % MOD) % MOD;
	for(int i = 1; i <= n; i++){
		int temp = 1;
		while(a[i] == a[i + 1]){
			i++; temp++;
		}
		for(int j = 1; j <= temp; j++) ans = (1ll * ans * inv[j] % MOD) % MOD;
	}
	printf("%d\n", ans);
	return 0;
}

T2 pack

  一个神奇 玄学 的 DP,然而我还没弄出来 我觉得 T3 比 T2 简单


T3 weight

  这次 T3 考思维,但是数据太水了导致暴力枚举都能过 50 分,我们来模拟一遍:

  1. 首先把数组排序,然后在数组中间找到一个 m 使得数组被分成两部分,一组里面较小: a 1 < a 2 < ⋯ < a m a_1 < a_2 < \cdots < a_m a1<a2<<am,另一组里面较大: a m < a m + 1 < ⋯ < a n a_m < a_{m+1} < \cdots < a_n am<am+1<<an。然后我们利用这个数组的有序性来解决这个问题(不要问我 m 是怎么来的,等会看完过程就知道了)。
  2. 然后对于每次操作分成两种情况,一种是和上一次的操作一致,另一种是和上一次的操作不同。
  3. 对于和上一次的操作一致的情况(假设都是 L),那么如果上一次我们在左边放了一个 a i a_i ai 那么这一次我们就在右边放一个 a i − 1 a_{i-1} ai1
  4. 对于和上一个操作不同的情况(假设前一次是 L 这一次是 R)。我们这次就在右边的那一堆里放一个 a j a_j aj 使得它刚好大于左边里面的最大的一个。

  我们可以发现这样的方法可以使得左右两边每次都有一一对应的值,如果这次的操作中 L > R 那么左边就有 a i + 1 a_{i+1} ai+1 对应有边的 a i a_i ai。如果本次操作中 L < R,那么右边就有 a i + 1 a_{i+1} ai+1 对应左边的 a i a_i ai

#include<bits/stdc++.h>
using namespace std;
#define endl '\n'
#define MAXN 100100
#define in read()

inline int read(){
	int x = 0; char c = getchar();
	while(c < '0' or c > '9') c = getchar();
	while('0' <= c and c <= '9'){
		x = x * 10 + c - '0'; c = getchar();
	}
	return x;
}

int n = 0;
int a[MAXN] = { 0 };
char s[MAXN];

int b[MAXN] = { 0 };
int c[MAXN] = { 0 };

int main(){
	n = in;
	for(int  i = 0; i < n; i++) a[i] = in;
	scanf("%s", s);
	sort(a, a + n); a[n] = 2e9;
	int m = -1; int op = 0;
	for(int i = 0; i < n; i++)
		if(i and s[i] == s[i-1]) m++;
	int k = m + 1;
	for(int i = 0; i < n; i++){
		if(i and s[i] == s[i-1]){
			c[i] = op ^= 1; b[i] = a[m--];
		}
		else{
			c[i] = (s[i] == 'R'); b[i] = a[k++];
			if(!i) op = (s[i] == 'R');
		}
	}
	for(int i = 0; i < n; i++){
		cout << b[i] << ' ' << ("LR"[c[i]]) << endl;
	}
	return 0;
}

T4 swap

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值