2022暑期个人排位赛第二场
A. Minimize the Permutation
https://codeforces.ml/group/MKpYqfAQQQ/contest/388332/problem/A
题意
给你一个数字序列 你可以进行n-1次相邻两个数换位操作
求操作完后字典序最小的序列 每个位置只能移动一次
思路
想要字典序最小 就必须把小的数尽量往前移 那么我们只要按1 2 3 ...的顺序将找到它们的当前位置然后将他们往前移 直到不能移动或操作数用完为止
#include<iomanip>
#include<bits/stdc++.h>
#include<iostream>
#include<bitset>
#include<algorithm>
#include<cstring>
#include<cmath>
#include<map>
#include<vector>
#define ll long long
#include<unordered_map>
//#define ll int
#define ull unsigned long long
#define IOS ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
using namespace std;
const ll inf = 0x3f3f3f3f;
const ll INF = 0x3f3f3f3f3f3f3f3f;
const double eps = 1e-10;
const ll N = 4e5 + 7;
const int M = 1e6 + 5;
const ll mod = 2147493647;
ll n, m, a[105], b[105], c[105];
void solve() {
cin >> n;
ll sum = 0;
memset(c, 0, sizeof c);
for(int i = 1; i <= n; i++){
cin >> a[i];
//记录数a[i]再第几个位置
b[a[i]] = i;
}
ll cnt = 0;
for(int k = 1; k <= n; k++){
if(cnt == n - 1) break;
//从当前位置向前移
for(int i = b[k]; i > 1; i--){
if(!c[i - 1] && a[i - 1] > a[i]){
swap(a[i] , a[i - 1]);
c[i - 1] = 1;
cnt++;
//更新位置
b[a[i]] = i;
b[a[i - 1]] = i - 1;
}
else break;
}
}
//输出排好序的
for(int i = 1; i <= n; i++){
cout << a[i] <<" \n"[i == n];
}
}
signed main() {
IOS;
int t = 1;
cin >> t;
while (t--) {
solve();
}
}
B. Fight with Monsters
https://codeforces.ml/group/MKpYqfAQQQ/contest/388332/problem/B
题意
给出n个怪兽的血量 以及先手和后手的攻击力 先手可以有k次机会让后手停一轮
先手最后打死怪兽就可以的到一个分数 否则没分
求先手最多能得几分
思路
记录先手最后打死一个怪兽要用最少几次机会装入b数组中 然后将b数组排序 遍历b数组求和 求和小于等k的最大个数
#include<iomanip>
#include<bits/stdc++.h>
#include<iostream>
#include<bitset>
#include<algorithm>
#include<cstring>
#include<cmath>
#include<map>
#include<vector>
#define ll long long
#include<unordered_map>
//#define ll int
#define ull unsigned long long
#define IOS ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
using namespace std;
const ll inf = 0x3f3f3f3f;
const ll INF = 0x3f3f3f3f3f3f3f3f;
const double eps = 1e-10;
const ll N = 2e5 + 7;
const int M = 1e6 + 5;
const ll mod = 2147493647;
ll n, a[N], b[N], x, y, k;
void solve() {
cin >> n >> x >> y >> k;
for(int i = 1; i <= n; i++){
cin >> a[i];
//正好多个回合后怪兽被打死 那么后手左后打死 先手最后打完 怪兽还有y滴血
if(!(a[i] % (x + y))) {
b[i] = ceil(y / (double)x);
}
//多个回合后 不足一个回合 如果怪兽剩余的血 先手不能一次击败 那就要用机会了
else if(a[i] % (x + y) > x) {
b[i] = ceil((a[i] % (x + y) - x) / (double)x);
//cout << b[i] << '\n';
}
}
sort(b + 1, b + 1 + n);
ll sum = 0, cnt = 0;
for(int i = 1; i <= n; i++){
if(sum + b[i] <= k){
cnt++;
sum += b[i];
}
}
cout << cnt << "\n";
}
signed main() {
IOS;
int t = 1;
//cin >> t;
while (t--) {
solve();
}
}
C. p-binary
https://codeforces.ml/group/MKpYqfAQQQ/contest/388332/problem/C
题意
定义关于x的二进制 是类似 $$2^k + x$$的数
给出 n , m求n至少由多少个关于m的二进制数相加的到 如果一个都没有输出-1
思路
因为数据再1e9且一组输入 可以直接从小到大枚举个数 判断是否可行 输出答案即可
可以先减去x个m 然后计算x-m有几位为二进制数 对应一下就好了 (二进制最多32位 很好算)
#include<iomanip>
#include<bits/stdc++.h>
#include<iostream>
#include<bitset>
#include<algorithm>
#include<cstring>
#include<cmath>
#include<map>
#include<vector>
#define ll long long
#include<unordered_map>
//#define ll int
#define ull unsigned long long
#define IOS ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
using namespace std;
const ll inf = 0x3f3f3f3f;
const ll INF = 0x3f3f3f3f3f3f3f3f;
const double eps = 1e-10;
const ll N = 2e5 + 7;
const int M = 1e6 + 5;
const ll mod = 2147493647;
ll n, m;
bool check(ll x, ll k){
ll cnt = 0;
for(int i = 32; i >= 0; i--){
if((1ll<<i) & x) cnt++;
}
//cout << cnt << " " << cnt2 << '\n';
if(k >= cnt && k <= x) return true;
return false;
}
void solve() {
cin >> n >> m;
ll ans = INF;
for(ll i = 1; i <= 32; i++){
ll x = n - i * m;
if(x <= 0) break;
if(check(x, i)) {
ans = i;
break;
}
}
if(ans == INF) cout << -1 << "\n";
else cout << ans << '\n';
}
signed main() {
IOS;
int t = 1;
//cin >> t;
while (t--) {
solve();
}
}
D. Peaceful Rooks
https://codeforces.ml/group/MKpYqfAQQQ/contest/388333/problem/D
题意
给定棋盘大小和棋子数 (棋盘是正方形的)
再给出棋子的坐标
要求同一行和同一列不能有两颗棋子 棋子一次可以水平竖直移任意多格 求棋子最后从左上到右下的对角线的最少步数
思路
可以得出 像 12 21 | 12 23 31 | 12 23 34 41 这几组棋子会冲突不能直接移 必须有一颗先移到它们所占据的范围外其他棋子才能动 这样花费就是 它们的个数+1
有可以的出上述那样的棋子 的横竖坐标就是一个并查集 那么我们只要求这样的并查集的 将并查集的大小加一贡献给答案即可 特别注意还有剩余的几颗单独的棋子 如果不在对角线上就也要移动一次
#include<iomanip>
#include<bits/stdc++.h>
#include<iostream>
#include<bitset>
#include<algorithm>
#include<cstring>
#include<cmath>
#include<map>
#include<vector>
#define ll long long
#include<unordered_map>
#define ull unsigned long long
#define IOS ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
using namespace std;
const ll inf = 0x3f3f3f3f;
const ll INF = 0x3f3f3f3f3f3f3f3f;
const double eps = 1e-10;
const ll N = 2e5 + 5;
const int M = 1e6 + 5;
const ll mod = 2147493647;
ll n, m, fa[N], sz[N];
ll find(ll x) {
return x == fa[x] ? x : fa[x] = find(fa[x]);
}
void solve() {
cin >> n >> m;
ll x, y;
//初始化
for (int i = 1; i <= n; i++) {
fa[i] = i;
sz[i] = 1;
}
ll s = m, ans = 0;
for (int i = 1; i <= m; i++) {
cin >> x >> y;
if (x == y) {
s--;
continue;
}
ll xx = find(x);
ll yy = find(y);
if (xx != yy) {
fa[xx] = yy;
sz[yy] += sz[xx];
}
else {
ans += sz[xx] + 1;
s -= sz[xx];
}
}
cout << ans + s << "\n";
}
signed main() {
IOS;
int t = 1;
cin >> t;
while (t--) {
solve();
}
}
E. Make The Fence Great Again
https://codeforces.ml/group/MKpYqfAQQQ/contest/388333/problem/E
题意
给你n个数 要求让相邻的两个数不相等 可以进行多次操作 每次操作可以将第i个数加1但要花费b[i]
求最小花费
思路
dp
经探究一个数最多加二 那么我们只要开一个二维dp[i][0]:第i个数不加 dp[i][1]第i个数加1 dp[i][2]:第i个数加2
枚举对于第i个 枚举j k 代表第i个要只增加多少 第i-1个增加了多少 判断它们高度是否相同 如果相同
那就可以转移 \(dp[i][j] = min(dp[i - 1][k] + j * b[i], dp[i][j])\)
#include<iomanip>
#include<bits/stdc++.h>
#include<iostream>
#include<bitset>
#include<algorithm>
#include<cstring>
#include<cmath>
#include<map>
#include<vector>
#define ll long long
#include<unordered_map>
#define ull unsigned long long
#define IOS ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
using namespace std;
const ll inf = 0x3f3f3f3f;
const ll INF = 0x3f3f3f3f3f3f3f3f;
const double eps = 1e-10;
const ll N = 3e5 + 5;
const int M = 1e6 + 5;
const ll mod = 2147493647;
ll n, a[N], b[N], dp[N][3];
void solve() {
cin >> n;
for (int i = 1; i <= n; i++) {
cin >> a[i] >> b[i];
//在里面初始化 防超时
dp[i][0] = dp[i][1] = dp[i][2] = INF;
}
//memset(dp, INF, sizeof dp);
//初始化 第一个根据增加情况dp值直接知道
dp[1][0] = 0;
dp[1][1] = b[1];
dp[1][2] = b[1] * 2;
for (int i = 2; i <= n; i++) {
for (int j = 0; j <= 2; j++) {
for (int k = 0; k <= 2; k++) {
if (a[i - 1] + j != a[i] + k) {
dp[i][k] = min(dp[i][k], dp[i - 1][j] + b[i] * k);
}
}
}
}
cout << min({ dp[n][0], dp[n][1], dp[n][2] }) << "\n";
}
signed main() {
IOS;
int t = 1;
cin >> t;
while (t--) {
solve();
}
}
F. AB-string
https://codeforces.ml/group/MKpYqfAQQQ/contest/388333/problem/F
题意
给定一个只含有A B的字符串 判断它有多少个子串 满足 该串的所有字符都可已在一个长度大于1 的它的子串中存在
思路
可以看出来只有ABBBB AAAAB BAAAA BBBBBA 这样的字符串不合法
正反个遍历一遍 寻找这样的字符串一共有多少个 用(n + 1)n/2减去 再减去n即可 注意ABBBB AAAAB会都会记一次AB所以计两者的其中之一时要减一 (BAAAA BBBBBA同理)
#include<iomanip>
#include<bits/stdc++.h>
#include<iostream>
#include<bitset>
#include<algorithm>
#include<cstring>
#include<cmath>
#include<map>
#include<vector>
#define ll long long
#include<unordered_map>
#define ull unsigned long long
#define IOS ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
using namespace std;
const ll inf = 0x3f3f3f3f;
const ll INF = 0x3f3f3f3f3f3f3f3f;
const double eps = 1e-10;
const ll N = 3e5 + 5;
const int M = 1e6 + 5;
const ll mod = 2147493647;
ll n;
string s;
void solve() {
cin >> n;
cin >> s;
ll cnt = 0, f = 0, ans = 0, cnt2 = 0;
for (int i = 0; i < n; i++) {
if (s[i] == 'A') {
cnt2++;//计算AAAB
ans += cnt;
cnt = 0;
f = 1;
}
else {
if (f) cnt++;//计算ABBBB
if(cnt2) ans += cnt2 - 1;
cnt2 = 0;
}
}
ans += cnt;
//反向计算BBBA BAAAA
cnt = 0, f = 0, cnt2 = 0;
for (int i = n - 1; i >= 0; i--) {
if (s[i] == 'A') {
cnt2++;
ans += cnt;
cnt = 0;
f = 1;
}
else {
if (f) cnt++;
if(cnt2) ans += cnt2 - 1;
cnt2 = 0;
}
}
ans += cnt;
//cout << ans << '\n';
cout << (1 + n) * n / 2 - ans - n << "\n";
}
signed main() {
IOS;
int t = 1;
//cin >> t;
while (t--) {
solve();
}
}
H. Wi-Fi
https://codeforces.ml/group/MKpYqfAQQQ/contest/388333/problem/H
题意
1-n个房间 每个房间可以选择装wifi或者宽带 花费都是i wifi可以接受的范围是i-k到i+k如果能接到wifi了可以不装宽带
求每个房间都不断网的最小花费
思路
用动态规划 dp[i]代表前i中情况不断网的最小花费
对于每每个房间有两种情况
1.只能装宽带 前面没有wifi能遍历到 即dp[i] = dp[i - 1] + i
2.前面有wifi可覆盖到该地方 就找最前面的可接受的wifi的位置 假设为p 那么p-k 到 p+k的位置都有那个wifi的影响这段花费为0 那到当前位置的花费就可以由dp[p-k-1]转移过来
所以转移方程为 dp[i] = min(dp[i - 1] + i, dp[p - k - 1] + p);
#include<bits/stdc++.h>
#include<iostream>
#include<vector>
#include<unordered_map>
#define ll long long
#define ull unsigned long long
#define IOS ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
using namespace std;
const ll inf = 0x3f3f3f3f;
const ll INF = 0x3f3f3f3f3f3f3f3f;
const double eps = 1e-10;
const ll N = 5e5 + 5;
const int M = 1e6 + 5;
const ll mod = 1e9 + 7;
ll n, m, dp[N], near[N];
string s;
void solve() {
cin >> n >> m;
cin >> s;
dp[0] = 0;
//初始化
for (int i = 1; i <= n; i++) {
dp[i] = INF;
}
near[n + 1] = -1;
//求离当前房间最近的wifi位置
for (int i = n; i >= 1; i--) {
if (s[i - 1] == '1') near[i] = i;
else near[i] = near[i + 1];
}
for (int i = 1; i <= n; i++) {
ll pre = near[max(1ll, i - m)];
if (pre < 0) dp[i] = dp[i - 1] + i;
//因为dp初始化为无穷大了所以如果pre-m-1在当前位置的后面 dp值为无穷大取min也没影响
else dp[i] = min(dp[i - 1] + i, dp[max(0ll, pre - m - 1)] + pre);
}
cout << dp[n] << "\n";
}
signed main() {
IOS;
int t = 1;
//cin >> t;
while (t--) {
solve();
}
}
I. Strange Housing
https://codeforces.ml/group/MKpYqfAQQQ/contest/388333/problem/I
题意
给一无向图 要求选几个点满足 这每两个点都不是相邻的 对于一条边 只有至少一个端点被选才是有用的(即如果一条边的两个点都没被选 那就相当于这条边被删除)
要求图是连通的(仅对有用边而言)
若没有方案则输出no 否则输出yes从小到大输出选择的点
思路
染色法 将第一个点标记为已选
用bfs遍历它的儿子节点 如果父亲节点为已选 那么儿子节点都要标记为不选 如果父亲节点为不选 那么只有没被访问过的点才被标记为已选
因为标记为已选的点可能后续又会被标记为未选 所以在bfs完后 再遍历一遍节点 存答案 如果存在点未被访问过 则说明该图为非连通图直接输出no
#include<bits/stdc++.h>
#include<iostream>
#include<vector>
#include<unordered_map>
#define ll long long
#define ull unsigned long long
#define IOS ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
using namespace std;
const ll inf = 0x3f3f3f3f;
const ll INF = 0x3f3f3f3f3f3f3f3f;
const double eps = 1e-10;
const ll N = 5e5 + 5;
const int M = 1e6 + 5;
const ll mod = 1e9 + 7;
ll n, m;
//flag[i]标记i节点是否被选择 vis[i]标记是否访问过i节点
ll flag[N], vis[N];
vector<ll>g[N], ans;
queue<ll>q;
void bfs(ll x) {
q.push(x);
vis[x] = 1;
while (!q.empty()) {
ll now = q.front();
q.pop();
for (auto to : g[now]) {
if (!flag[now]) {//如果上一个节点为未选 只有未被访问过的点标记为已选(不然可能导致相邻的已选点)
if (!vis[to]) {
flag[to] = 1;
vis[to] = 1;
q.push(to);
}
}
else {//如果上一个节点为已选 那么与它相邻的点一定不能选(可能之前标记为已选但必须标记回未选)
flag[to] = 0;
if (!vis[to]) {
q.push(to);
vis[to] = 1;
}
}
}
}
}
void solve() {
ans.clear();
cin >> n >> m;
ll x, y;
for (int i = 1; i <= n; i++) {
g[i].clear();
flag[i] = 0;
vis[i] = 0;
}
for (int i = 1; i <= m; i++) {
cin >> x >> y;
g[x].push_back(y);
g[y].push_back(x);
}
flag[1] = 1;
bfs(1);
for (int i = 1; i <= n; i++) {
if (!vis[i]) {
cout << "NO\n";
return;
}
if (flag[i]) ans.push_back(i);
}
sort(ans.begin(), ans.end());
cout << "YES\n";
cout << ans.size() << "\n";
for (int i = 0; i < ans.size(); i++) {
cout << ans[i] << " \n"[i == ans.size() - 1];
}
}
signed main() {
IOS;
int t = 1;
cin >> t;
while (t--) {
solve();
}
}