Codeforces Round #696 (Div. 2)

比赛地址c++

A(水题)

题目连接
数组

题目:
给出一个01串,如今要求求得另外一个相同长度01串,使得两个串相加(容许为2),去除连续相同子序列后的三进制数最大spa

解析:
首先长度优先级确定大于高位数字大小的优先级,因此要保证不被去除任何连续相同子序列的状况下,让高位数尽量地大,因而能够考虑贪心的加1(即若是和上一位相加的结果相同则加0)code

#include<bits/stdc++.h>
using namespace std;
char str[100005];

int main() {
	int T, n;
	scanf("%d", &T);
	while (T--)
	{
		scanf("%d%s", &n, str + 1);
		char last = 0;
		for (int i = 1; str[i]; ++i)
		{
			char t = '1';
			if (str[i] + t == str[i - 1] + last)
				--t;
			printf("%c", last = t);
		}
		printf("\n");
	}
}

B(水题)

题目连接
get

题目:
给出一个\(d\),找出最小的天然数\(x\)知足,\(x\)至少含有4个因子(包括1和自己)且\(x\)全部因子之差大于等于\(d\)it

解析:ast

  1. 很显然为了让\(x\)最小,那么必定保证4个因子中有1和自己两个因子
  2. 因为题目中要求全部因子都得大于等于\(d\),那么若是选择\(1+d,1+2d\)做为中间两个因子的状况下,若是两者不为素数,则两者的因数组合成的数(也就是\(x\))的因数不能保证差大于等于\(d\)
  3. 因此选定的数必需要保证是一对素数(其实经过打表也能够发现),这样即可以先预处理出全部素数,每次找出符合条件的素数因数便可
#include<bits/stdc++.h>
using namespace std;
/*===========================================*/
const int maxn = 5e4;
int prime[maxn];
bool vis[maxn];
int cnt;

void euler(){
	for (int i = 2; i < maxn; ++i){
		if (!vis[i]) prime[cnt++] = i;
		for (int j = 0; j < cnt && prime[j] * i < maxn; ++j){
			vis[i * prime[j]] = true;
			if (i % prime[j] == 0)
				break;
		}
	}
}

int main(){
	euler();
	int T, n;
	scanf("%d", &T);
	while (T--){
		scanf("%d", &n);
		int* a = lower_bound(prime, prime + cnt, 1 + n);
		int* b = lower_bound(a + 1, prime + cnt, *a + n);
		printf("%lld\n", (LL)(*a) * (*b));
	}
}

C(思惟)

题目连接
⭐⭐class

题目:
给出\(2n\)个数,初始能够指定任意的数做为基数,而后将数组中和为基数的两个数删除,较大的数做为新的基数,问如此往复是否能够删除全部的数,并输出每轮的基数test

解析:im

  1. 经过推理,若是删除最大的数,那么基数必定是比这个最大的数和其余任意数的组合,在删除完后最大的数成为了基数,那么若是现存数中最大的数没法被删除,那么基数必定会被一个比现存最大的数更小的数所替代,那么这个现存数种最大的数永远没法被删除
  2. 那么就能够得知除了第一轮的基数(即最大的数和其余数的组合)是没法肯定的之外,后续结果都已经固定(必须删除次大的数),检验过程经过检测现存的数中是否存在基数减去最大数的值便可,所以进\(O(n)\)的检验是否出现彻底删除的状况便可

注意:

  1. 若是使用\(multiset\)存储当前剩余的数,必定要先从中删除最大的数,再去进行检测,避免若是两者值相等的状况下,会误认为结果仍能够成立
  2. 不使用\(set\)用桶进行存储查询也是一样的作法
#include<bits/stdc++.h>
using namespace std;
/*===========================================*/
const int maxn = 2005;
const int M = 1e6 + 5;
int dat[maxn];
multiset<int, greater<int>> s;
vector<pair<int, int>> ans;
int n, T;

int main()
{
	scanf("%d", &T);
	while (T--)
	{
		scanf("%d", &n);
		for (int i = 1; i <= 2 * n; ++i)
			scanf("%d", &dat[i]);
		sort(dat + 1, dat + 2 * n + 1);
		for (int i = 1; i < 2 * n; ++i)
		{
			ans.clear();
			s.clear();
			int last = dat[2 * n];
			ans.push_back(pair<int, int>(dat[2 * n], dat[i]));
			for (int j = 1; j < 2 * n; ++j) if (j != i) s.insert(dat[j]);
			while (!s.empty())
			{
				multiset<int, greater<int>>::iterator t;
				int tmp = *s.begin();
				s.erase(s.begin());
				if ((t = s.find(last - tmp)) != s.end())
				{
					ans.push_back(pair<int, int>(tmp, *t));
					last = tmp;
					s.erase(t);
				}
				else
					break;
			}
			if (ans.size() == n)
				break;
		}
		if (ans.size() == n)
		{
			printf("YES\n%d\n", ans[0].first + ans[0].second);
			for (auto& i : ans)
				printf("%d %d\n", i.first, i.second);
		}
		else
			printf("NO\n");
	}
}

D(思惟)

题目连接
⭐⭐⭐

题目:
给出一组数,相邻的数值\(a,b\)能够减去\(\min(a,b)\),这个操做能够进行任意次,同时还能够在操做前交换一次相邻的数值,问是否能够将这组数清空?

解析:

  1. 对于这个删除操做,考虑将第一个数清空,则必须经过第二个数来实现,那么此时的第二个数的清空又必需要依靠后续的数,同理倒数第一个数、第二个数也是这样
  2. 那么若是选择\((i-1,i)\)进行交换,那么\(pre[i-2]\)\(suf[i+1]\)的部分是必定不会受到影响的,也就是说对于\(2\times(n-1)\)种交换的可能性种只须要检测\(pre[i-2],\underbrace{i,i+1}_{swap},suf[i+1]\)是否能够成当即可
  3. 对于已经没法成立的部分其\(pre\)\(suf\)\(-1\)表示,且对于\(pre\)若是遇到\(-1\)那后续必然都是\(-1\),\(suf\)若是遇到\(-1\)则前序必然都是\(-1\)

注意:在 0 与 n+1 的位置赋了 0 避免特判处理

#include<bits/stdc++.h>
#define rep(i,a,b) for(int  i=a;i<b;++i)
#define rep1(i,a,b) for(int  i=a;i<=b;++i)
#define rrep(i,a,b) for(int i=b-1;i>=a;--i)
#define rrep1(i,a,b) for(int i=b;i>=a;--i)
typedef long long LL;
using namespace std;
/*===========================================*/
const int maxn = 2e5 + 5;
int dat[maxn];
int pre[maxn], suf[maxn];

bool check(int i)
{
	return dat[i - 1] >= pre[i - 2] && dat[i] >= suf[i + 1] && dat[i - 1] - pre[i - 2] == dat[i] - suf[i + 1];
}

int main()
{
	int T;
	scanf("%d", &T);
	while (T--)
	{
		int n;
		scanf("%d", &n);
		rep1(i, 1, n)
		{
			scanf("%d", &dat[i]);
			if (pre[i - 1] < 0 || dat[i] < pre[i - 1])
				pre[i] = -1;
			else
				pre[i] = dat[i] - pre[i - 1];
		}
		suf[n + 1] = 0;
		rrep1(i, 1, n)
			if (suf[i + 1] < 0 || dat[i] < suf[i + 1])
				suf[i] = -1;
			else
				suf[i] = dat[i] - suf[i + 1];
		bool ok = false;
		rep1(i, 2, n)
		{
			if (pre[i - 2] == -1 || suf[i + 1] == -1) continue;
			if (ok = check(i)) break;
			swap(dat[i - 1], dat[i]);
			if (ok = check(i)) break;
			swap(dat[i - 1], dat[i]);
		}
		printf("%s\n", ok ? "YES" : "NO");
	}
}

E(思惟构造)

题目连接
⭐⭐⭐

题目:
给出一个长度\(n\),给出操做\((i,j),p[j]=i\Rightarrow swap(i,j)\),但要付出\((i-j)^2\)的代价,现须要求出对应的长度为\(n\)的排列可能拥有的最大代价、排列自己以及中间的操做过程

解析:

  1. 能够分析得出,交换可使得当前值与值对应的位置进行交换,为了使交换的位置距离尽量原,能够朴素的想到构造出形如\(2,3,4,\dots,n,1\)的序列,这样\((1,n)(2,n)\dots(n-1,n)\)不断进行交换,但这样却不是最优的
  2. 那么能够考虑构造形如\(n-1,3,4,\dots,\lfloor n/2\rfloor,1,n,\lfloor n/2\rfloor+1,\lfloor n/2\rfloor+2,\dots,n-2,2\)的序列,这样\((n-1,1)(n-2,1)\dots(\lfloor n/2\rfloor+1,1),(2,n)(3,n)\dots(\lfloor n/2\rfloor,n),(n,1)\)进行交换,这样不可贵出所须要的次数是\((n-1)^2+2*\left((n-2)^2+\dots+(n-\lfloor n/2\rfloor)^2\right)\),再判断是否\(\lfloor n/2\rfloor\)为奇数加上\((n-1-\lfloor n/2\rfloor)^2\)
  3. 其实上述的构造方式至关于第一种朴素构造的变形,从两边开始找尽量长的交换路径(即两端非端点的位置开始向内部进行交换)

注意:

  1. 要开\(long\ long\)
  2. \(n=2,n=3\)的状况进行特判
#include<bits/stdc++.h>
#define rep(i,a,b) for(int  i=a;i<b;++i)
#define rep1(i,a,b) for(int  i=a;i<=b;++i)
#define rrep(i,a,b) for(int i=b-1;i>=a;--i)
#define rrep1(i,a,b) for(int i=b;i>=a;--i)
typedef long long LL;
using namespace std;
/*===========================================*/

const int maxn = 1e5 + 5;
LL sum[maxn];

int main() {
	rep(i, 1, maxn)
		sum[i] = sum[i - 1] + 1LL * i * i;
	int T;
	scanf("%d", &T);
	while (T--) {
		long long n;
		scanf("%lld", &n);
		long long t = n / 2;
		if (n == 2)
			printf("1\n2 1\n1\n2 1\n");
		else if (n == 3)
			printf("5\n2 3 1\n2\n2 1\n1 3\n");
		else
		{
			printf("%lld\n", (n - 1) * (n - 1) + 2 * (sum[n - 2] - sum[n - 1 - t]) + (n & 1 ? (n - 1 - t) * (n - 1 - t) : 0));
			printf("%lld", n - 1);
			rep(i, 2, t)
				printf(" %d", i + 1);
			printf(" 1 %lld", n);
			rep(i, t + 2, n)
				printf(" %d", i - 1);
			printf(" 2\n");
			printf("%lld\n", n - 1);
			rep1(i, 2, t)
				printf("%d %lld\n", i, n);
			rrep(i, t + 1, n)
				printf("%d 1\n", i);
			printf("%lld 1\n", n);
		}
	}
}
相关文章
相关标签/搜索