1. 程式人生 > 實用技巧 >Educational Codeforces Round 100 (Rated for Div. 2)

Educational Codeforces Round 100 (Rated for Div. 2)

被教育專場教育了,嗚嗚嗚

沙拉查詞和谷歌翻譯誤我啊!找到一個好用的翻譯Deepl,這個翻譯不會吧\(Latex\)翻譯錯。

A - Dungeon

\(3\)個怪,生命值分別是\(a,b,c\),普通攻擊對其中一隻怪\(hp-1\),每\(7\)次可以對所有怪\(hp-1\)此時必須3只怪都還有生命值),問殺死所有怪的最後一擊能否是特殊攻擊。

第一次被翻譯坑是特殊攻擊翻譯成\(hp-11\),第二次是沒翻譯好特殊攻擊必須全部都打

\(3\)只怪生命值求和,因為普通攻擊可以隨便打,所以只需要看和\(\%9\)(一輪攻擊)是不是等於\(0\),注意特判生命值最小的怪是不是提前被打死

B - Find The Array

構造題+思維

給定\([a_1, a_2, \dots, a_n]1 \le a_i \le 10^9\),求陣列\(b\)滿足

  • \(1 \le b_i \le 10^9\)
  • \(b_i\)能整除\(b_{i+1}\)\(b_{i+1}\)能整除\(b_i\)或兩者都可
  • \(2 \sum \limits_{i = 1}^{n} |a_i - b_i| \le S\)\(S= \sum_{i=1}^na_i\)

觀察條件\(2\),可以通過中間插入一些\(1\),來輕鬆實現,例如\([1,b_2,1,b_3,1,b_4,......]\)

接下來問題在於如何滿足條件\(3\)

這個條件用人話來說就是要求兩個陣列差距儘量小,顯然如果對於任意\(i\)

都有\(a_i=b_i\),那差距就是\(0\)

結合一些上面就能輕鬆得出構造方法\([1,a_2,1,a_4,.....]\)

當然上面都是思考的過程,並沒有嚴謹的論證,下面是來自parfe211的證明:

假設\(n\)是偶數。設\(X=[1,a[2],1,a[4],...]\)\(Y=[a[1],1,a[3],1,...]\)。那麼對於\(X\)\(W_{X}=2\sum\limits_{i=1}^{n}|a_{i}-X_{i}|=a_{1}+a_{3}+ \dots - \frac{n}{2}\),對於\(Y\)\(W_{Y}=2\sum\limits_{i=1}^{n}|a_{i}-Y_{i}|=a_{2}+a_{4}+ \dots - \frac{n}{2}\)

那麼這個表示式的和是\(a_{1}+a_{2}+ \dots +a_{n}-n=S-n\)。但\(S-n=W_X+W_Y\)。因此,\(W_X\)\(W_Y\)中至少有一個數小於或等於\(\frac{S-n}{2}\)

仍要注意的是需要開\(longlong\)

C - Busy Robot

大模擬

非常噁心,還沒寫/kk

D - Pairs

思維+二分

給定 $2n $個整數 \(1,2,...,2n\)。將這\(2n\)個元素重新分配成\(n\)對。選擇\(x\)對,並從其中取最小元素,並從其他\(n-x\)對中取最大元素。選出的元素構成集合\(b\)。給定\(n\),和集合\(b\),問有多少個\(x\)能構成\(b\)

關鍵的結論:

方法\(1\)

找到最大的\(x\)和最小的\(x\),中間的\(x\)也都可以構成\(b\)集合

考慮兩個陣列\(v_1,v_2\)\(v_1\)包含\(b\)中的數字,\(v_2\)包含不在\(b\)中的數字,兩個陣列都按遞增順序排序。

定義\(cmp(l_1,r_1,l_2,r_2)\)如下。

如果可以將\(v_1[l_1...r_1]\)\(v_2[l_2...r_2]\)重新分配成\((r_1-l_1+1)\)對,使得每一對陣列中,\(v_1\)的數字都小於\(v_2\)的數字,那麼\(cmp(l_1,r_1,l_2,r_2)=-1\)\(v_1\)都大則返回\(1\),否則返回\(0\)

當且僅當\(cmp(0,x-1,n-x,n-1)=-1\)\(cmp(x,n-1,0,n-x-1)=1\)時,則某個\(x\)可以得到\(b\)

序列\(cmp(0,0,n-1,n-1),cmp(0,1,n-2,n-1),...,cmp(0,n-1,0,n-1)\)是不遞減的。因此,滿足\(cmp(0,x-1,n-x,n-1)=-1\)的最大\(x\)可以通過二元搜尋找到。

同理,最小的\(x\)也可以找到,答案就是\(x_{max}-x_{min}+1\)

/*
@ author:pyyyyyy
-----思路------
本程式碼存在bug,謹慎參考,明天再改改
-----debug-------

*/

#include <bits/stdc++.h>
#include <vector>
using namespace std;

inline int read() {
    char c = getchar();
    int x = 0, f = 1;
    while(c < '0' || c > '9') {
        if(c == '-') f = -1;
        c = getchar();
    }
    while(c >= '0' && c <= '9') x = x * 10 + c - '0', c = getchar();
    return x * f;
}

const int N = 1e5 + 2077;
int T, n;
vector<int> v1, v2;
int v[N];

int cmp(int l1, int r1, int l2, int r2){
	int flag1 = 1, flag2 = 1;
	for(int i = 0; i <= r1 - l1; ++i){
		if(v1[l1 + i] > v2[l2 + i]) flag1 = 0;
		else if(v1[l1 + i] < v2[l2 + i]) flag2 = 0;
	}
	if(flag1 == 0 && flag2 == 0) return 0;
	else  if(flag1 == 1) return -1;
	else return 1;
}

int main()
{
	//	freopen(".in","r",stdin);
//		freopen("test.out","w",stdout);
	T = read();
	while(T--){
		int minn = 0, maxn = 0;
		v1.clear(), v2.clear();
		n = read();
		for(int i = 0; i <= 2 * n; ++i) v[i] = 0;
		for(int i = 1; i <= n; ++i) {
			int x = read();
			v[x] = 1;
			v1.push_back(x);
		}
		for(int i = 1; i <= 2 * n; ++i)
			if(v[i] == 0) v2.push_back(i);
			
		if(cmp(1, n, 1, n) == -1) maxn = n;
		else if(cmp(1, 1, n, n) != -1) maxn = 0;
		else {
			int l = 1, r = n;
			while(l <= r){
				int mid = (l + r) >> 1;
				if(cmp(1, mid, n - mid + 1, n) == -1) l = mid + 1, maxn = mid;
				else r = mid - 1;
			}
		}
		
		if(cmp(n, n, 1, 1) != 1) minn = n;
		else if(cmp(1, n, 1, n) == 1) minn = 0;
		else {
			int l = 1, r = n;
			while(l <= r){
				int mid = (l + r) >> 1;
				if(cmp(n - mid + 1, n, 1, mid) == 1) l = mid + 1, minn = mid;
				else r = mid - 1;
			}
		}
		cout << maxn - minn + 1 << '\n';
	}
	return 0;
}
/*
input:

output:

*/

方法\(2\)

和群友交流後,有大佬提出可以用田忌賽馬的方法思考本題,我太菜了,不懂

......未完待續......