T1 Bogo
终于有一道我会做的期望题了,太感动了。随机排序到升序的概率就是:
p
=
∏
i
=
1
k
c
i
n
!
p = \frac{\prod\limits_{i=1}^kc_i}{n!}
p=n!i=1∏kci
其中
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+(1−p)(E+1)=p+E+1−pE−p→E=p1
于是:
E
=
n
!
∏
i
=
1
k
c
i
E = \frac{n!}{\prod\limits_{i=1}^kc_i}
E=i=1∏kcin!
对于上面那个玩意儿,显然是 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 分,我们来模拟一遍:
- 首先把数组排序,然后在数组中间找到一个 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 是怎么来的,等会看完过程就知道了)。
- 然后对于每次操作分成两种情况,一种是和上一次的操作一致,另一种是和上一次的操作不同。
- 对于和上一次的操作一致的情况(假设都是 L),那么如果上一次我们在左边放了一个 a i a_i ai 那么这一次我们就在右边放一个 a i − 1 a_{i-1} ai−1
- 对于和上一个操作不同的情况(假设前一次是 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;
}