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();
	}

}
posted @ 2022-07-07 10:41  Yaqu  阅读(50)  评论(0)    收藏  举报