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);
		}
	}
}
相關文章
相關標籤/搜索