暑假排位赛1

A. Space Formula

https://codeforces.ml/group/MKpYqfAQQQ/contest/387755/problem/A

题意

第一行给出n个人的当前分数 下一行给出下一场比赛第一第二第三..第n 可以的得到的分数
两行都按从大到小的顺序给出
求当前第k位的人下一场比赛后最优可以排第几

思路

首先指定的人下一场拿第一肯定是最优的
然后找排名比他前面的那个人那最高分但不比指定人的总分高 重复操作 b数组依次从前往后找
算出最多能超过几人

#include<iomanip>
#include<bits/stdc++.h>
#include<iostream> 
#include<bitset>
#include<algorithm>
#include<cstring>
#include<cmath>
#include<map>
#include<vector>
#define ll long long
//#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, p, a[N], b[N];

void solve() {
	cin >> n >> p;
	for(int i = 1; i <= n; i++){
		cin >> a[i];
	}
	for(int i = 1; i <= n; i++){
		cin >> b[i];
	}
	if(p == 1){
		cout << 1 << "\n";
		return;
	}
	ll s = a[p] + b[1];
	int  j = 2;
	for(j = 2; j <= n; j++){
		if(b[j] + a[p - 1] <= s) break;
	}
	if(j > n) {
		cout << p << "\n";
		return;
	}
	ll kk = p;
	for(int i = p - 1, k = j; i >= 1 && k <= n; i--){
		if(a[i] + b[k] <= s) {
			kk = i;
			k++;
		}
		else{
			while(a[i] + b[k] > s && k <= n){
				k++;
			}
			if(k <= n) {
				kk = i;
				k++;
			}
		}
	}
	cout << kk << "\n";
}


signed main() {
	IOS;
	int t = 1;
	//cin >> t;
	while (t--) {
		solve();
	}

}

B. Polycarp Restores Permutation

https://codeforces.ml/group/MKpYqfAQQQ/contest/387753/problem/B

题意

给出一个序列q 求一个序列p 其中p是只含有1-n的排列 并满足q[i] = p[i + 1] - p[i]

思路

设p[1]为x 那么p[2] = x + q[1] p[3] = x + q[1] + q[2]...
而p[1] + p[2] + ... + p[n] = (1 + n) * n / 2
只有x不知道 求出x就可以求出p序列
要注意不存在的情况
数字不重复 且在1-n之间

#include<iomanip>
#include<bits/stdc++.h>
#include<iostream> 
#include<bitset>
#include<algorithm>
#include<cstring>
#include<cmath>
#include<map>
#include<vector>
#define ll long long
//#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, p, a[N], pre[N], b[N];
map<ll, ll>mp;

void solve() {
	cin >> n;
	ll sum = 0;
	a[1] = 0;
	pre[1] = 0;
    for(int i = 2; i <= n; i++){
    	cin >> a[i];
    	pre[i] = pre[i - 1] + a[i];
    	sum += pre[i];
	} 
	ll f = ((1 + n) * n / 2 - sum ) / n;
	//cout << mi << " " <<f << "\n";
	if(f <= 0 || ((1 + n) * n / 2 - sum ) % n){
		cout << -1 << "\n";
		return;
	}
	for(int i = 1; i <= n; i++){
		b[i] = f + pre[i];
		if(b[i] <= 0 || b[i] > n || mp[b[i]]) {
			cout << -1 << "\n";
			return;
		}
		mp[b[i]] = 1;
	}
	for(int i = 1; i <= n; i++){
		cout << b[i] << " \n"[i == n];
	}
	
}


signed main() {
	IOS;
	int t = 1;
	//cin >> t;
	while (t--) {
		solve();
	}

}

C. MP3

https://codeforces.ml/group/MKpYqfAQQQ/contest/387755/problem/C

题意

给出n个数
K代表不同的数的个数 一个数需要的空间是[log2k]位 给出I字节空间
问可以最少改变多少个数 使得它们的到小在l r之间且能被装下([l,r]自己定)

思路

首先得给数排个序
然后开两个数组 num1[i]:排序后第i个数前面的数都变成a[i]要变几个数 num2[i]:排序后第i个数后面的数都变成a[i]要变几个数
用map类型的cnt[a[i]]表示遍历到第i个 a[i]出现的次数 num1[i] = i - cnt[a[i]]
同理 num2[i] = n - i + 1 - cnt2[a[i]]
最后用双指针求得最优答案即可
这题要注意的是 a[i]可到1e9 用map计算个数时 时间复杂度较高 需要离散化 因为n最大4e5可以把大数与小数一一对应

#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, xx, k, a[N], b[N];
ll num1[N], num2[N], mp[N], cnt1[N], cnt2[N];

//离散化
void change() {
	ll tot = 1;
	a[1] = 1;
	for (int i = 2; i <= n; i++) {
		if (b[i] != b[i - 1]) tot++;
		a[i] = tot;
	}
}

void solve() {
	cin >> n >> xx;
	for (int i = 1; i <= n; i++) {
		cin >> b[i];
	}
    sort(b + 1, b + 1 + n);
	change();

	ll k = (8 * xx) / n;
	if (k >= 20) {
		cout << 0 << "\n";
		return;
	}
	ll kk = (1ll << k);
	if (kk >= n) {
		cout << 0 << "\n";
		return;
	}
	for (int i = 1; i <= n; i++) {
		cnt1[a[i]]++;
		num1[i] = i - cnt1[a[i]];
	}
	for (int i = n; i >= 1; i--) {
		cnt2[a[i]]++;
		num2[i] = n - i + 1 - cnt2[a[i]];
	}

	ll cnt = 0;
	for (int i = 1; i <= kk; i++) {
		if (!mp[a[i]]) cnt++;
		mp[a[i]]++;
	}
	ll l = 1, r = kk;
	ll ans = INF;
        //双指针
	while (l <= r && r <= n) {
		if (cnt <= kk) {
			ans = min(ans, num1[l] + num2[r]);
			r++;
			if (mp[a[r]] == 0) cnt++;
			mp[a[r]]++;
		}
		else {
			mp[a[l]]--;
			if (mp[a[l]] == 0) cnt--;
			l++;
		}
	}
	cout << ans << "\n";
}


signed main() {
	IOS;
	int t = 1;
	//cin >> t;
	while (t--) {
		solve();
	}

}

D. Anadi and Domino

https://codeforces.ml/group/MKpYqfAQQQ/contest/387755/problem/D

题意

有21张牌 头尾个一个数 分别是 11 12 13 14 15 16 21 22 23 24 25 26 31 32 33 34 35 36
41 42 43 44 45 46 51 52 53 54 55 56 61 62 63 64 65 66
给出n个点n不超过7
接下来m条边 每条边只能放一张牌 对向每个点的数要一样
求最多有几条边能放牌

思路

当n<7的时候每条边肯定都能放牌 直接输出m
当n == 7时 先默认某六个点的边已经放满牌编号为1-6 假如将第7个点编号为1 然后遍历编号为2-5的点看两个编号为1的点是否都与零5个点有边
若是 那就必定要删一条边
用三层for循环实现 第一层将其中一个点选为第7个点 第二层将第7个点以前六个中的某个编号相同 第三层遍历前五个点判断是否要删边
先把与第7个点相连的边边删去最后一层遍历的时候加边也可 这里还要额外记录每个边的入度 然后再以i点为第7点时 m-in[i]即可然后再后面加边

#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;
map<ll, ll>mp[10];
ll in[20];

void solve() {
	cin >> n >> m;
	ll x, y;
	for (int i = 1; i <= m; i++) {
		cin >> x >> y;
		mp[x][y] = 1;
		in[y]++;
		mp[y][x] = 1;
		in[x]++;
	}
	if (n <= 6) {
		cout << m << '\n';
		return;
	}
	ll ans = 0;
	for (int i = 1; i <= 7; i++) {
		ll x = m - in[i];
		for (int j = 1; j <= 7; j++) {
			ll cnt = 0;
			if (i == j) continue;
			if (mp[i][j]) cnt++;
			for (int k = 1; k <= n; k++) {
				if (k == i || k == j) continue;
				if (mp[i][k] && !mp[j][k]) cnt++;
			}
			ans = max(ans, x + cnt);
		}
	}
	cout << ans << "\n";
}


signed main() {
	IOS;
	int t = 1;
	//cin >> t;
	while (t--) {
		solve();
	}

}

E. Tokitsukaze, CSL and Stone Game

https://codeforces.ml/group/MKpYqfAQQQ/contest/387755/problem/E

题意

n堆石子 两人博弈先后谁便拿一个石子 如果一个人没有石子拿了或者拿完石子后有两堆石子数量一样了就算输

思路

特判先手一开始就必输的情况 :
1.一开始全为0
2.有多于两堆一样的石子
3.有多对 一样的石子 例 1 1 2 2
4.有两堆0个石子
5.有两堆n个石子但存在n-1个石子的石子堆
特判外 之后最终情况肯定是 0 1 2 3 ..n这样的情况 所以判断 石子总数-(n-1)n/2的奇偶即可

#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, xx, k, a[N], b[N];
map<ll, ll>mp, vis;

void solve() {
	cin >> n;
	ll cnt0 = 0, f = 1, sum = 0;
	for (int i = 1; i <= n; i++) {
		cin >> a[i];
		sum += a[i];
		mp[a[i]]++;
		if (mp[a[i]] >= 3) {
			f = 0;
			break;
		}
		if (!a[i]) cnt0++;
	}
	if (f == 0 || cnt0 >= 2 || cnt0 == 1 && n == 1) {
		cout << "cslnb\n";
		return;
	}
	ll cnt = 0;
	for (int i = 1; i <= n; i++) {
		if (vis[a[i]]) continue;
		vis[a[i]] = 1;
		if (mp[a[i]] >= 2) cnt++;
		if (mp[a[i]] >= 2 && mp[a[i] - 1]) {
			cout << "cslnb\n";
			return;
		}
	}
	if (cnt > 1) {
		cout << "cslnb\n";
		return;
	}
	if ((sum - (n - 1) * n / 2) % 2) cout << "sjfnb" << "\n";
	else cout << "cslnb\n";
}


signed main() {
	IOS;
	int t = 1;
	//cin >> t;
	while (t--) {
		solve();
	}

}

F. Hills

https://codeforces.ml/group/MKpYqfAQQQ/contest/387755/problem/F

题意

有n个土堆 一个土堆比相邻土堆高度都高 就可以建房 每次挖土机可以将一个土堆降低一米
求建 1 - [n/2]个房 分别要用几次挖土机

思路

dp
dp[i][j][0]代表前i个土堆建了j个房子且第i个土堆不建房要的花费
dp[i][j][1]代表前i个土堆建了j个房子且第i个土堆建房要的花费
因为必须个一个土堆建房 所以第i位建房了 第i- 1位一定不建房 所以第i位可以由第i-2位的情况转移过来

\[dp[i][j][0] = min(dp[i - 1][j][0], dp[i - 1][j][1] + max(0, a[i] - a[i - 1] + 1)) \]

\[dp[i][j][1] = min(dp[i - 2][j - 1][0] + max(0, a[i - 1] - a[i] + 1), dp[i - 2][j - 1][1] + max(0, a[i - 1] - min(a[i - 2], a[i]) + 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 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 = 5e3 + 5;
const int M = 1e6 + 5;
const ll mod = 2147493647;
ll n, a[N], dp[N][N][2];

void solve() {
	cin >> n;
	for (int i = 1; i <= n; i++) {
		cin >> a[i];
	}
    memset(dp, inf, sizeof dp);

	for (int i = 1; i <= n; i++) {
		dp[i][0][0] = 0;
	}

	dp[1][1][1] = 0;
	dp[2][0][0] = 0;
	dp[2][1][0] = max(0, a[2] - a[1] + 1);
	dp[2][1][1] = max(0, a[1] - a[2] + 1);

	for (int i = 3; i <= n; i++) {
		for (int j = 1; j <= (i + 1) / 2; j++) {
			dp[i][j][0] = min(dp[i - 1][j][0], dp[i - 1][j][1] + max(0, a[i] - a[i - 1] + 1));
			dp[i][j][1] = min(dp[i - 2][j - 1][0] + max(0, a[i - 1] - a[i] + 1), 
				dp[i - 2][j - 1][1] + max(0, a[i - 1] - min(a[i - 2], a[i]) + 1));
		}
	}
	
	for (int i = 1; i <= (n + 1) / 2; i++) {
		cout << min(dp[n][i][0], dp[n][i][1]) << " \n"[i == (n + 1) / 2];
	}
}


signed main() {
	IOS;
	int t = 1;
	//cin >> t;
	while (t--) {
		solve();
	}

}

G. Leaving Auction

https://codeforces.ml/group/MKpYqfAQQQ/contest/387755/problem/G

题意

多个人拍卖从低到高竞价
输入 x y x为哪个人 y为价格
后n行每次有m个人离开
问最后谁赢得拍卖 以多少价格赢的 一个人以自己能赢的最低拍卖价赢
输出 人的编号和价格如果没人赢就输出0 0

思路

用vector存每个人的竞拍价格将一个人的最高竞拍价代表这个人 用mp实现 并计入在set中
每次询问 记入走的人 在set中删去
set中最后一个即为赢者 然后二分查找赢者比最后第二个人最高竞价大的最低竞价 为最后答案
如果set中为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 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, mx[N];
vector<ll>g[N];
unordered_map<ll, ll>mp;
set<ll>st;

void solve() {
	cin >> n;
	ll x, y;
	for (int i = 1; i <= n; i++) {
		cin >> x >> y;
		g[x].push_back(y);
		mx[x] = y;//记录最高价
		mp[y] = x;//最高价代表一个人的身份 
	}
	for (int i = 1; i <= n; i++) {
		if (mx[i]) {
			st.insert(mx[i]);
			mp[mx[i]] = i;
		}
	}
	cin >> m;
	ll xx, yy;
	vector<ll>v;
	for (int i = 1; i <= m; i++) {
		cin >> xx;
		v.clear();
                //删去缺席者
		for (int j = 1; j <= xx; j++) {
			cin >> yy;
			v.push_back(yy);//存删去的人 后续还要加
			if (mx[yy]) 
			    st.erase(mx[yy]);
		}

		if (st.size() == 0) cout << "0 0" << "\n";
		else if (st.size() == 1) cout << mp[*st.begin()] << " " << g[mp[*st.begin()]][0] << "\n";
		else {
			auto it = st.end();
			it--;
			ll nn = *it;
			it--;
                        //二分找最低赢的竞价
			ll ans = *(upper_bound(g[mp[nn]].begin(), g[mp[nn]].end(), *it));
			cout << mp[nn] << " " << ans << "\n";
		}
                //多次询问 最后将删去的人再加回来
		for (int j = 0; j < xx; j++) {
			if(mx[v[j]])
			    st.insert(mx[v[j]]);
		}
	}
}


signed main() {
	IOS;
	int t = 1;
	//cin >> t;
	while (t--) {
		solve();
	}

}

H. We Need More Bosses

https://codeforces.ml/group/MKpYqfAQQQ/contest/387755/problem/H

题意

给一个图 随机两个点 a b, 求从a走到b一定要走的边最少是多少

思路

在一个强连通图中 其中的边都不是一定要选的
那么我们吧强连通的图缩小成一个缩点 最后新建一个图 相当于一颗树 这棵树的直径长就是答案

#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 = 3e5 + 5;
const int M = 1e6 + 5;
const ll mod = 2147493647;
ll n, m;
//bel[i]包含i的缩点
ll dfn[N], low[N], ins[N], bel[N], dis[N], cnt;
stack<ll>st;
//相应缩点中存在的点
vector<ll>ecc[N];
vector<ll>edge[N];
vector<ll>edge2[N];
ll ec, tot;
//找强连通图 并缩点
void tarjan(ll s, ll fa) {
	dfn[s] = low[s] = ++tot;
	ins[s] = 1, st.push(s);
	for (auto to : edge[s]) {
		if (!dfn[to]) {
			tarjan(to, s);
			low[s] = min(low[to], low[s]);
		}
		else if (to != fa) {
			low[s] = min(low[s], low[to]);
		}
	}

	if (dfn[s] == low[s]) {
		++ec;
		while (1) {
			ll u = st.top();
			st.pop();
			ins[u] = 0;
			bel[u] = ec;
			ecc[ec].push_back(u);
			if (u == s) break;
		}
	}
}

void dfs(ll x, ll fa)
{
	for (ll to : edge2[x])
	{
		if (to != fa)
		{
			dis[to] = dis[x] + 1;
			dfs(to, x);
		}
	}
}
//求直径
ll diameter()
{
	for (ll i = 1; i <= n; i++)
		dis[i] = 0;
	dfs(1, -1);
	ll mx = 0, st = 1;
	for (ll i = 1; i <= n; i++)
		if (dis[i] > mx)
			mx = dis[i], st = i;
	for (ll i = 1; i <= n; i++)
		dis[i] = 0;
	dfs(st, -1);
	mx = 0;
	for (ll i = 1; i <= n; i++)
		mx = max(dis[i], mx);
	return mx;
}

void solve() {
	cin >> n >> m;
	ll x, y;
	for (int i = 1; i <= m; i++) {
		cin >> x >> y;
		edge[x].push_back(y);
		edge[y].push_back(x);
	}
	tarjan(1, 0);
	for (int i = 1; i <= n; i++) {
		for (auto to : edge[i]) {
			if (bel[i] == bel[to]) continue;
			edge2[bel[i]].push_back(bel[to]);
		}
	}
	cout << diameter() << "\n";
}


signed main() {
	IOS;
	int t = 1;
	//cin >> t;
	while (t--) {
		solve();
	}

}
posted @ 2022-07-04 11:01  Yaqu  阅读(96)  评论(0)    收藏  举报