A. 小红的双排列
小红很喜欢双排列。
现在小红拿到了一个数 n ,请你帮他构造一个长度为 2×n 的双排列。
双排列:长度为 2×n 的双排列为两个长度为 n 的排列打乱顺序后得到的数组。
解题思路:
1...n/1...n
#include <bits/stdc++.h>
using namespace std;
using ll = long long;
void solve() {
int n;
cin >> n;
for (int i = 1; i <= n; i++) {
cout << i << " " << i;
if (i < n)
cout << " ";
}
}
int main() {
int t = 1;
// cin >> t;
while (t--) {
solve();
}
return 0;
}
B. 小红的双排列拆分
小红拿到了一个长为 2×n 的双排列。现在他想从中取两个不相交子序列,使两个子序列都是长度为 n 的排列,请你帮帮他。
两个子序列是不相交的,当且仅当它们从原序列中取元素的位置没有重叠,即下标不同。
解题思路:从长度为2*n的输入数据中取出两个长度为n的排列
#include <bits/stdc++.h>
using namespace std;
using ll = long long;
void solve() {
int n;
cin >> n;
vector<int> a(2 * n);
for (int i = 0; i < 2 * n; i++) {
cin >> a[i];
}
vector<int> vis(n + 1, 0);
vector<int> s1, s2;
for (int x : a) {
if (!vis[x]) {
s1.push_back(x);
vis[x] = 1;
} else {
s2.push_back(x);
}
}
for (int x : s1)
cout << x << " ";
cout << '\n';
for (int x : s2)
cout << x << " ";
cout << '\n';
}
int main() {
int t = 1;
// cin >> t;
while (t--) {
solve();
}
return 0;
}
C.小红的双排列删除
小红拿到了一个长为 2×n 的双排列 {a1,a2,...,a2×n} 。
小芳能帮他进行任意次如下操作:
选择一个首尾元素相等的区间 [l,r](1≦l<r≦2×n; al=ar) ,将 al,al+1,...,ar 这段元素删除,并将其余元素按现有顺序拼接起来。
小红想知道,在可以进行任意次上述操作的情况下,能否将双排列中的所有数删除。
解题思路:
对于一个首尾相同的子区间是可以进行删除的
1 2 3 1 2 3
无法完全删除, 如果删除[1..1],会剩下[2,3]
1 2 3 3 2 1可以删除, [1,1]
由于每个数字都会出现两次, 只要[1..1]中有另外的数字, 那么就一定无法完全删除
因此我们可以模拟删除的过程,
先倒着遍历, 记录当前数字最后出现的位置
然后正向模拟删除的过程, 如果当前数字后续没有数字了, 那就删不掉了
输出"No"
#include <bits/stdc++.h>
using namespace std;
using ll = long long;
int a[500050];
void solve() {
int n;
cin >> n;
// vector<int> a(2 * n);
int m=-1;
for (int i = 0; i < 2 * n; i++){
cin >> a[i];
}
map<int,int> mp;
for(int i=2*n-1;i>=0;i--){
if(mp[a[i]]==0) mp[a[i]]=i;
}
int p=0;
for(int i=0;i<2*n;){
if(mp[a[i]]>i){
i=mp[a[i]]+1;
}else{
p=1;
break;
}
}
if(p) cout<<"No"<<'\n';
else cout<<"Yes"<<'\n';
}
// 0 3
// 1 4
int main() {
ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);
int t;
cin >> t;
while (t--) {
solve();
}
return 0;
}
D.小红的双排列权值
对于给定的长度为 2×m 的双排列 {b1,b2,...,b2×m},对于每个 k,若其两次出现的位置为 l 和 r(1≦l<r≦2×m),则定义 f(k)=r−l−1.
小红将双排列的权值定义为每一对相同元素的下标距离之和,即:
f(i){i->(1..m)}
现在小红拿到了一个长为 2×n 的双排列 {a1,a2,...,a2×n}
小芳可以帮他进行最多一次如下操作(也可以不操作):
选择下标 l,r (1≦l,r≦2×n), 交换 al,ar
请你帮小红求出可得到的最大权值。
解题思路:
让两个相同数字尽可能的离得远一些
对于交换操作
对于两个分离的子区间[1..1] [2...2] [3...3]
如果交换了1和3
[1..3] [2...2] [1...3]
交换后多出了两个中间区间
[L1,R1] [L2.R2]
swap(R1,L2)
由于只能swap一次, 所以我们就要找最左侧的R1,和最右侧的L2
#include <bits/stdc++.h>
using namespace std;
using ll = long long;
void solve() {
int n;
cin >> n;
vector<int> L(n + 1, n * 2);
int L1 = 2 * n + 1, r = 0;
int ans = 0, x;
for (int i = 1; i <= n * 2; i++) {
cin >> x;
L[x] = min(L[x], i);
if (L[x] < i) {
ans += (i - L[x] - 1);
L1 = min(L1, i);
} else {
r = max(r, L[x]);
}
}
cout << ans + max(0LL, (ll)(r - L1) * 2) << '\n';
}
int main() {
int t = 1;
// cin >> t;
while (t--) {
solve();
}
return 0;
}
E.小红的双排列删除得分
小红拿到了一个长为 2×n 的双排列 {a1,a2,...,a2×n} 。
小芳能帮他进行任意次如下操作:
选择一个首尾元素相等的区间 [l,r](1≦l<r≦2×n; al=ar) ,将 al,al+1,...,ar 这段元素删除,并将其余元素按现有顺序拼接起来,同时小红将获得 ∑i=lra分。
请你帮小红求出可能的最高得分。
解题思路:
dp[i]: 前i个数所能得到的最高得分
只要当前数字的左端点存在就考虑删还是不删
不删的话dp[i]=dp[i-1]
删的话就是dp[i]=max(dp[i-1]+区间和)
#include <bits/stdc++.h>
using namespace std;
using ll = long long;
void solve() {
ll n;
cin >> n;
vector<ll> a(n * 2 + 1), L(n + 1, n * 2), s(n * 2 + 1);
for (ll i = 1; i <= n * 2; i++) {
cin >> a[i];
s[i] = s[i - 1] + a[i];
L[a[i]] = min(L[a[i]], i);
}
vector<ll> dp(n * 2 + 1);
for (ll r = 1; r <= n * 2; r++) {
ll l = L[a[r]];
dp[r] = dp[r - 1];
if (l < r) {
dp[r] = max(dp[r], dp[l - 1] + (s[r] - s[l - 1]));
}
}
cout << dp[n * 2] << '\n';
}
int main() {
int t = 1;
// cin >> t;
while (t--) {
solve();
}
return 0;
}
F.小红的双排列期望(easy)
小红拿到了一个长度为 2×n 的数组 {a1,a2,…,a2×n},初始所有元素都是 0,她可以进行任意次以下操作:
∙尝试使 ai 加 1。该操作有 bi 的概率成功。若成功,则 ai 加 1;若失败,则无任何变化。。
小红希望最终 a 变成一个双排列。她希望最小化操作次数,请你求出小红最优策略下,最终操作次数的期望。答案对 109+7 取模。
解题思路:
从i-1到i的期望步数是1/p%
从0到i i/p%
大概率对应的大数,所以大概率尽量往后放
#include <bits/stdc++.h>
using namespace std;
const int N = 1e6 + 5;
#define int long long
const int c = 1e9 + 7;
int q_p(int a, int b) {
int ans = 1;
a %= c;
while (b) {
if (b & 1)
ans = ans * a % c;
a = a * a % c;
b >>= 1;
}
return ans % c;
}
void solve() {
int n;
cin >> n;
vector<int> a(n * 2);
for (int i = 0; i < n * 2; i++)
cin >> a[i];
sort(a.begin(), a.end());
int ans = 0;
int j = 100;
for (int i = 0; i < n * 2; i++) {
ans += j * q_p(a[i], c - 2) % c;
ans %= c;
if (i % 2)
j += 100;
}
cout << ans << '\n';
}
signed main() {
int t = 1; // cin>>t;
while (t--){
solve();
}
return 0;
}
G .小红的双排列期望(hard)
小红拿到了一个长度为 2×n 的数组 {a1,a2,…,a2×n},初始所有元素都是 000,她可以进行任意次以下操作:
,∙尝试使 ai 加 1。该操作有 bi% 的概率成功。若成功,则 ai加 1;若失败,如果 ai 大于 0 则会减 1(等于 0 则无变化)。
小红希望最终 a 变成一个双排列。她希望最小化操作次数,请你求出小红最优策略下,最终操作次数的期望。答案对 109+7 取模。
解题思路:
设f(k)为概率为 p%时,从k−1变为kk的操作次数期望
则f(k)=p%∗1+(1−p%)∗(1+f(k−1)+f(k))p%的概率 走1步到达
(1−p%)的概率 走1步回到k−2
再走f(k−1)步到k−1
再走f(k)f步到k
化简得f(k)=[(1−p%)∗f(k−1)+1]/p%=[(100−p)∗f(k−1)+100]/p对其再求前缀和 得到从0变为各个数的操作次数期望
概率只有100种可能性 可以直接全部预处理
#include <bits/stdc++.h>
using namespace std;
const int M = 1e9 + 7;
int b[200005];
long long dp[101][100005];
long long q_p(long long a, int k) {
long long res = 1;
a %= M;
while (k > 0) {
if (k & 1)
res = res * a % M;
a = a * a % M;
b >>= 1;
}
return res % M;
}
void solve(){
int n;
cin >> n;
for (int i = 1; i <= 100; i++) {
for (int j = 1; j <= n; j++) {
long long inv = q_p(i, M - 2);
dp[i][j] = ((100 - i) * dp[i][j - 1] % M + 100) % M * inv % M;
}
}
for (int i = 1; i <= 100; i++) {
for (int j = 1; j <= n; j++) {
dp[i][j] = (dp[i][j] + dp[i][j - 1]) % M;
}
}
for (int i = 1; i <= n * 2; i++) {
cin >> b[i];
}
sort(b + 1, b + 1 + n * 2);
long long ans = 0;
for (int i = 1; i <= n * 2; i++) {
ans = (ans + dp[b[i]][(i + 1) / 2]) % M;
}
cout << ans << '\n';
}
int main(){
int t = 1;
while(t--){
solve();
}
return 0;
}
感谢大家的点赞和关注,你们的支持是我创作的动力!