【題解】 Codeforces Round #670 (Div. 2)


人生巔峰,這是我第一次 \(\textrm{AK div2}\)

一場 \(\textrm{div2}\) 讓我信心大增,感謝中國同胞!!!

Link \(\textrm{to Codeforces}\)

Contest

A. Subset Mex

Legend

給定長度為 \(n\ (1 \le n \le 100)\) 的數組 \(a\ (0 \le a_i \le 100)\),把它們分成兩個集合,使得 \(\operatorname{mex}(S_1)+\operatorname{mex}(S_2)\) 盡量大,可以為空。

數據組數 \(1 \le t \le 100\)

Editorial

顯然貪心構造即可。

Code

可以但沒必要。

B. Maximum Product

Legend

給定長度為 \(n\ (5 \le n,\sum n \le 10^5)\) 的數組 \(a\ ( |a_i| \le 3 \times 10^3)\)。從中選 \(5\) 個數字,使得乘積盡可能大。

數據組數 \(1 \le t \le 2\cdot10^4\)

Editorial

顯然排完序只能選兩端的。

Code

可以但沒必要。

Legend

給定一棵樹,共 \(n\ (3 \le n,\sum n \le 10^5)\) 個節點,請刪除一條邊再加上一條邊,使得圖依然聯通且樹的重心有且僅有一個。

數據組數 \(1 \le t \le 10^4\)

Editorial

找到重心。

  • 如果只有一個,隨便斷一條邊再連上。
  • 如果有兩個,則它們肯定不是葉子,那么把其中的一個重心任選一個子樹接到另一個重心去即可。

Code

int c1 ,c2 ,sz[MX] ,mx[MX] ,n;
void dfs(int x ,int f){
	mx[x] = 0;
	sz[x] = 1;
	for(int i = head[x] ,d ; i ; i = h[i].next){
		if((d = h[i].node) == f) continue;
		dfs(d ,x);
		sz[x] += sz[d];
		mx[x] = max(mx[x] ,sz[d]);
	}
	mx[x] = max(mx[x] ,n - sz[x]);
	if(mx[x] == mx[c1]) c2 = x;
	if(mx[x] < mx[c1]){
		c1 = x;
		c2 = 0;
	}
}
 
void solve(){
	mx[0] = 114514;
	cin >> n;
	for(int i = 1 ; i <= n ; ++i) head[i] = 0;
	tot = c2 = c1 = 0;
	int u ,v;
	for(int i = 1 ; i < n ; ++i){
		cin >> u >> v;
		addedge(u ,v);
	}
	dfs(1 ,0);
	if(!c2){
		cout << u << " " << v << endl;
		cout << u << " " << v << endl;
	}
	else{
		
		for(int i = head[c1] ,d ; i ; i = h[i].next){
			if((d = h[i].node) != c2){
				cout << c1 << " " << d << endl;
				cout << d << " " << c2 << endl;
				break;
			}
		}
	}
}

D. Three Sequences

Legend

給定長度為 \(n\ (1 \le n \le 10^5)\) 的數組 \(a\ (|a_i| \le 10^9)\)

同時有另外兩個長度與 \(a\) 相同的數組 \(b,c\) 滿足:

  • \(b_i+c_i=a_i\)
  • \(b\) 單調不降。
  • \(c\) 單調不升。

你需要最小化 \(\max(b_i,c_i)\),輸出這個結果。

以及還有 \(q\ (1 \le q \le 10^5)\) 次修改,將 \(a\) 區間 \([l,r]\) 加上 \(x\ (|x| \le 10^9)\)

Editorial

好題!有那么點意思。

因為 \(b\) 單調不降,我們考慮如下式子:

\(b_2=b_1+ \Delta_1\ (\Delta \ge 0)\)\(b_1+c_1=a_1\)\(b_2+c_2=a_2\)\(c_1 \ge c_2\)

整理可得:\(a_1+\Delta_1 \ge a_2\)

我們肯定是希望 \(\sum \Delta\) 盡可能小,所以 \(\Delta\) 取到等號最優,即 \(\Delta_i=\max(0,a_{i+1}-a_i)\)

我們要求的答案是 \(\max(b_n,c_1)\),那我們得先把這個式子表示出來。

根據定義我們可以得到 \(b_n = b_1 + \sum \Delta\)。我們肯定是希望 \(c_1,b_n\) 比較接近,這樣才取到最優。

\(c_1=b_1+\sum \Delta\),整理得到 \(c_1 = \left\lceil \dfrac{a_1 + \sum \Delta}{2} \right\rceil\) 即為最終答案。

區間加只會改動差分數組的兩個位置,改變了 \(\Delta\) 的值。

Code

LL a[MX] ,del[MX] ,cost ,n;

void upd(int pos ,int v){
	if(pos > n || pos == 1) return;
	LL before = max(del[pos] ,0LL);
	del[pos] += v;
	cost += max(del[pos] ,0LL) - before;
}

LL Ans(LL x){
	if(x >= 0) return (x + 1) / 2;
	return x / 2;
}

void solve(){
	cost = 0;
	cin >> n;
	for(int i = 1 ; i <= n ; ++i){
		cin >> a[i];
		del[i] = a[i] - a[i - 1];
		if(i != 1 && del[i] >= 0) cost += del[i];
	}
	cout << Ans(a[1] + cost) << endl;
	int q; cin >> q;
	for(int i = 1 ; i <= q ; ++i){
		int l ,r ,d; cin >> l >> r >> d;
		if(l == 1) a[1] += d;
		else{
			upd(l ,d);
		}
		if(r != n){
			upd(r + 1 ,-d);
		}
		cout << Ans(a[1] + cost) << endl;
	}
}

E. Deleting Numbers

Legend

本題為交互題。

你有一個集合 \(\{1,2,\cdots,n\}\ (1 \le n \le 10^5)\),現在有一個數 \(x\ (1 \le x \le n)\),你要猜它。你可以對集合進行如下操作:

  • 詢問集合中有多少個 \(a\ (1 \le a \le n)\) 的倍數。
  • 詢問集合中有多少個 \(a\ (1 \le a \le n)\) 的倍數,並把 \(a\) 的倍數刪除。特別地,\(x\) 永遠不會被刪除。
  • 告訴交互器答案是 \(a\)。這個操作只能執行一次。

你最多可以操作 \(10^4\) 次。

Editorial

a useful web

這種題目嘛,所有人都會想到質數。於是我馬上進了 \(\textrm{number empire}\) 網站(Link),搜索了 \(10^5\) 以內的質數個數,發現很巧,正好有 \(9592\) 個,與題目限制的 \(10^4\) 十分接近。發現到答案有可能是個大質數,因為每一個質數都至少要做一次操作 \(2\),所以詢問次數的下界就是 \(9592\)我們還有 \(408\) 次可以做其他的事情。

a well-known conclusion

用到一個十分基礎的結論:\(> \sqrt{n}\) 的質因子最多只有一個。

for prime \(\le \sqrt{n}\)

我馬上發現 \(\sqrt{n}=316.227766\cdots\),發現 \(\le \sqrt{n}\) 的質數只有 \(65\) 個。我們不妨對這 \(65\) 個質數進行暴力操作,每次先刪了 \(p\) 的倍數,再詢問 $p1,p2,p^3\cdots $ 是否還有。發現這最多只需要 \(65+15\) 次就能搞定。(\(15\) 是因為 \(2^{17}>10^5\))。

for prime \(> \sqrt{n}\)

接下來對於 \(> \sqrt{n}\) 的質數還有 \(9528\) 個,但是這當中最多只有一個因子。我分了以下兩種情況討論:

  • 這個數存在 \(\le \sqrt{n}\) 的因子。

那么可以對每一個 \(> \sqrt{n}\) 的質數使用操作 \(1\),看元素數量對不對,沒對上就說明肯定這個 \(>\sqrt{n}\) 的因子就是當前詢問的。

  • 這個數不存在 \(< \sqrt{n}\) 的因子。

考慮每 \(\sqrt{9528}=97.611474\cdots\) 個質數分成一塊,每刪除完一個塊中所有數就檢查一下 \(1\) 的倍數有多少個(即集合元素數量),如果沒對上就說明這個因子一定在這個塊里面。暴力檢查塊內元素即可。最多消耗 \(98+98=196\) 次。

所以最壞情況消耗次數是 \(9592+65+15+98+98=9868\) 次,可以通過。

復雜度我不太會算,如果是枚舉 \(1 \to n\),那復雜度是 \(O(n \log n)\) 但只枚舉了質數,遠遠達不到。

**upd: ** 復雜度與埃氏篩是一樣的,即 \(O(n \log \log n)\)

Code

交互題調試還要自己寫交互器……

#include <bits/stdc++.h>

using namespace std;

#define LL long long

const int MX = 1e5 + 233;

int pri[MX] ,Npri[MX] ,n ,tot;
int vis[MX];
int shouldrem;

void sieve(){
	Npri[0] = Npri[1] = true;
	for(int i = 2 ; i <= n ; ++i){
		if(!Npri[i]) pri[++tot] = i ;
		for(int j = 1 ; j <= tot && pri[j] * i <= n ; ++j){
			int aim = pri[j] * i;
			Npri[aim] = 1;
			if(i % pri[j] == 0) break;
		}
	}
}

/*
int dele[MX] ,Ans;
int del(int x ,int v = 1){
	int ret = 0;
	for(int i = x ; i <= n ; i += x){
		ret += !dele[i];
		if(v == 0) continue;
		if(i != Ans) dele[i] = 1;
	}return ret;
}
*/
int AskB(int x){
	cout << "B " << x << endl;
	// return del(x);
	cin >> x; return x;
}

int AskA(int x){
	cout << "A " << x << endl;
	// return del(x ,0);
	cin >> x; return x;
}


int gar;
int main(){
	cin >> n;
	// Ans = 1949;
	shouldrem = n;
	sieve();
	
	int Ans = 1;
	int i = 1;
	for( ; i <= tot && pri[i] <= min(317 ,n) ; ++i){
		int p = pri[i];
		gar = AskB(p);
		for(int j = p ; j <= n ; j += p)
			shouldrem -= !vis[j] ,vis[j] = 1;
		
		int ok = AskA(p);
		if(ok) Ans *= pri[i];
		for(p *= pri[i] ; ok && p <= n ; p *= pri[i]){
			ok = AskA(p);
			if(ok) Ans *= pri[i];
		}
	}
	
	if(Ans != 1){
		for( ; i <= tot ; ++i){
			gar = AskB(pri[i]);
			int tmp = 0;
			for(int j = pri[i] ; j <= n ; j += pri[i])
				shouldrem -= !vis[j] ,tmp += !vis[j] ,vis[j] = 1;
			if(gar != tmp){
				Ans *= pri[i];
				break;
			}
		}
	}
	else{
		int cnt = 0;
		for( ; i <= tot ; ++i){
			++cnt;
			AskB(pri[i]);
			for(int j = pri[i] ; j <= n ; j += pri[i])
				shouldrem -= !vis[j] ,vis[j] = 1;
			if(cnt == 98 || i == tot){
				gar = AskA(1);
				if(gar != shouldrem){
					for(int j = 0 ; j < 98 ; ++j){
						gar = AskA(pri[i - j]);
						if(gar){
							Ans *= pri[i - j];
							goto out;
						}
					}
				}
				cnt = 0;
			}
		}
	}
	out:
	cout << "C " << Ans << endl;
	return 0;
}

Summary

是一場很有趣的 \(\textrm{div 2}\),雖說是中國場但沒有什么中國 \(\textrm{OI}\) 的氣息(指數據結構、多項式等)。考察的數學知識點較多,感覺 \(\textrm{PJ}\) 選手也能做起來友好的樣子。

我自己要反思一點是:\(\rm E\) 其實並不算太難,卻卡了我很久,有兩點原因:

  • 對自己沒有信心,高估 \(\textrm{2E}\) 難度,導致我想了半個小時沒有想出來就開始慌張。以至於算分塊復雜度的時候把質數數量看成了 \(10^5\),均值不等式一分析,詢問次數不夠……就更加慌張,還好最后發現了問題。
  • 對於交互題的不熟練,我測試樣例就用了很久時間。交互題的詢問什么的應該封裝到一個函數里,這樣子可以方便自己寫交互庫自己測試。我因為沒有調試導致損失了 \(150\) 分,不然的話就可以到 \(\textrm{rank5}\) 了。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM