CodeForces 1406 - Codeforces Round #670 (Div. 2)


(注:本來開了個坑,原標題為「do you know wtf it is???」,正文為「懵逼了吧」,所以被 hsc 罵了……)

這場比較簡單,所以本文的重心放在一波三折的比賽(賽后?)經歷上(

本來想着用 tzcWh... 這個號控制住不要上橙,大概控制在 2070~2099 左右,然后下一場 div. 2 就能超過大號這樣。

比賽結束前 5min E 交上去 WA 了,於是棄了,看了眼 predictor,能直接上 2150,這不行啊。於是開始故意 hack 失敗,網速比較慢所以 5min 只 hack 了 5 次。

減了 250pts 之后比賽結束,又看了一眼 predictor,+130?掐指一算,漲到 2102?wtf???要上橙就給我上高一點,要么就不要上橙,ntm 給我搭個橙名線是幾個意思?很后悔沒有早點開始 hack,多 hack 一次就 CM 穩了。然后就跟 wjz 瘋狂祖安。

然后突然發現我的 A 好像 FST 了,上面出現了個紅色的 -1?那 tgxl,rating 低一點沒關系,至少不用上橙了!然后 wjz 說「這是 feature,你刷新一下」(老 system test 觀眾了),然后又沒 FST,得分了。還我 FST!!!於是繼續祖安,祖安累了去洗了個澡。

洗完澡回來之后發現 wjz 跟我說「不會 master」,又是啥?system test 已經結束了,打開 predictor 一看 +124,2096?tgxl,這個結果我滿意。於是開開心心睡覺去了。

早上起來忐忑地打開電腦,發現居然 +127,2099?這無疑是我最滿意的結果,請叫我控分帶師(

也許這就是命運吧(


下面是題解(正文部分)

CF 比賽頁面傳送門

A - Subset Mex

洛谷題目頁面傳送門 & CF 題目頁面傳送門

給定一個集合 \(a,|a|=n\),將它分成兩個集合 \(A,B\),要求最大化 \(\operatorname{mex}(A)+\operatorname{mex}(B)\)。本題多測。

\(n\in[1,100],a_i\in[0,100],T\in[1,100]\)

隨便貪心就好了,兩個集合的 \(\operatorname{mex}\) 齊頭並進,某個進不了了就停下來,兩個都停下來的時候就是答案最大化的情況。代碼也隨便寫了吧。

#include<bits/stdc++.h>
using namespace std;
const int N=100;
int n;
int a[N+1];
int cnt[N+1];
void mian(){
	cin>>n;
	for(int i=1;i<=n;i++)cin>>a[i];
	sort(a+1,a+n+1);
	memset(cnt,0,sizeof(cnt));
	for(int i=1;i<=n;i++)cnt[a[i]]++;
	int fst=0;
	for(int i=0;i<=100;i++)if(cnt[i]<2){fst=i;break;}
	for(int i=fst;i<=100;i++)if(cnt[i]<1)return cout<<fst+i<<"\n",void();
	if(fst)cout<<fst+101<<"\n";
	else cout<<202<<"\n";
}
int main(){
	int testnum=1;
	cin>>testnum;
	while(testnum--)mian();
	return 0;
}

B - Maximum Product

洛谷題目頁面傳送門 & CF 題目頁面傳送門

給定 \(n\) 個整數(可正可負可零),求其中 \(5\) 個數的乘積的最大值。本題多測。

\(n\in\left[5,10^5\right],\sum n\leq 2\times10^5\)

這個就分個兩種情況吧,有 \(0\) 和無 \(0\)

\(0\) 的話就是 \(0\) 了。

\(0\) 的話就枚舉正數個數,然后排個序貪個心就切了。

#include<bits/stdc++.h>
using namespace std;
#define int long long
#define pb push_back
const int inf=0x3f3f3f3f3f3f3f3f;
int n;
void mian(){
	cin>>n;
	vector<int> po,ne;
	int ans=-inf;
	for(int i=1;i<=n;i++){
		int x;
		cin>>x;
		if(x>0)po.pb(x);
		if(x==0)ans=0;
		if(x<0)ne.pb(x);
	}
	sort(po.begin(),po.end(),greater<int>());
	sort(ne.begin(),ne.end());
	for(int i=0;i<=5;i++){
		if(i<=po.size()&&5-i<=ne.size()){
			int res=1;
			if(5-i&1){
				for(int j=(int)(po.size())-1;j>=(int)(po.size())-i;j--)res*=po[j];
				for(int k=(int)(ne.size())-1;k>=(int)(ne.size())-(5-i);k--)res*=ne[k];
			}
			else{
				for(int j=0;j<i;j++)res*=po[j];
				for(int k=0;k<5-i;k++)res*=ne[k];
			}
			ans=max(ans,res);
		}
	}
	cout<<ans<<"\n";
}
signed main(){
	int testnum=1;
	cin>>testnum;
	while(testnum--)mian();
	return 0;
}

洛谷題目頁面傳送門 & CF 題目頁面傳送門

給定一棵大小為 \(n\) 的樹。要求刪掉一條邊加上一條邊使重心唯一。給出方案。本題多測。

\(n\in\left[3,10^5\right],\sum n\leq 10^5\)

假如說本來就唯一的話不用說。否則:

顯然兩個重心相鄰。欽定其中一個為根,那么另一個就是第 \(2\) 層。我也不知道怎么想出來的,就隨便試吧,可以刪掉第 \(2\) 層重心的任意一條通向兒子的邊(由 \(n\geq 3\) 易證一定有兒子),然后把那個兒子連向根即可。

證明的話,顯然刪加過之后根依然是重心,因為子樹大小最大值變小了嘛。而第 \(2\) 層那個的相對應的顯然變大了,那么它們的最大子樹大小就不等了,就不可能同時為重心。然后如何證其他點不為重心呢?因為要想是重心就必須與根相鄰,而這三個點發生關系跟其他兒子有個屁的關系,那最大子樹大小肯定就不變啊,就無法翻身。得證。

#include<bits/stdc++.h>
using namespace std;
#define pb push_back
const int N=100000;
int n;
vector<int> nei[N+1];
int sz[N+1];
void dfs(int x=1,int fa=0){
	sz[x]=1;
	for(int i=0;i<nei[x].size();i++){
		int y=nei[x][i];
		if(y==fa)continue;
		dfs(y,x);
		sz[x]+=sz[y];
	}
}
void mian(){
	cin>>n;
	for(int i=1;i<=n;i++)nei[i].clear();
	for(int i=1;i<n;i++){
		int x,y;
		scanf("%d%d",&x,&y);
		nei[x].pb(y);nei[y].pb(x);
	}
	dfs();
	vector<int> cen;
	for(int i=1;i<=n;i++){
		bool flg=true;
		int sum=1;
		for(int j=0;j<nei[i].size();j++){
			int x=nei[i][j];
			if(sz[x]>sz[i])continue;
			flg&=sz[x]<=n/2;
			sum+=sz[x];
		}
		flg&=n-sum<=n/2;
		if(flg)cen.pb(i);
	}
	assert(cen.size()<=2);
	if(cen.size()==1)printf("%d %d\n%d %d\n",1,nei[1][0],1,nei[1][0]);
	else{
		int son=nei[cen[0]][0]==cen[1]?nei[cen[0]][1]:nei[cen[0]][0];
		printf("%d %d\n%d %d\n",cen[0],son,cen[1],son);
	}
}
int main(){
	int testnum=1;
	cin>>testnum;
	while(testnum--)mian();
	return 0;
}

D - Three Sequences

洛谷題目頁面傳送門 & CF 題目頁面傳送門

給定一個長度為 \(n\) 的數列 \(a\)。你需要構造出長度為 \(n\) 的數列 \(b,c\),滿足 \(a_i=b_i+c_i\),且 \(b\) 不降,\(c\) 不升。最小化 \(\max(b_i,c_i)\)。然后還有 \(q\) 次區間增加,每次輸出最小化的結果。

\(n,q\in\left[1,10^5\right]\)

二話不說先找結論啊。通過觀察樣例發現,一開始 \(b_1,c_1\) 隨便取只要滿足 \(a_1=b_1+c_1\) 即可,然后以后的話,若 \(a\) 的增量 \(\geq 0\) 就在 \(b\) 上加,否則就在 \(c\) 上減。證明的話隨便想想很簡單,顯然 \(\max(b_i,c_i)=\max(b_n,c_1)\),那么在 \(b_1,c_1\) 固定的時候,\(b\) 的總增量顯然越小越好,於是就只有在必要的時候才在 \(b\) 上加咯。

然后現在解決 \(b_1,c_1\) 不固定的事情。我們想要令 \(\max(b_n,c_1)\) 這個柿子最小,那么首先需要將 \(b_n\)\(b_1\) 表示一下。令增量 \(\Delta_i=a_{i+1}-a_i\),則 \(b_n=b_1+\sum\limits_{i=1}^{n-1}[\Delta_i\geq 0]\Delta_i\)。令那個 \(\sum\)\(\Sigma\),那么柿子為 \(\max(b_1+\Sigma,c_1)\)。又 \(a_1=b_1+c_1\),則柿子又可以寫為 \(\max(b_1+\Sigma,a_1-b_1)\)。那么注意到 \(\max\) 兩個參數是和為常量的,那么最理想的情況是令它們相等,則 \(\max\) 最小。解個方程可以得到 \(b_1=\dfrac{a_1-\Sigma}2\)。但是不允許出現小數,所以還要取個整然后左右兩邊 round 一下。

然后還有區間加呢。注意到區間加只會令兩個增量變化,於是隨便 \(\mathrm O(1)\) 維護即可。

#include<bits/stdc++.h>
using namespace std;
#define int long long
const int inf=0x3f3f3f3f3f3f3f3f;
const int N=100000;
int n;
int a[N+1];
int qu;
int d[N+1];
int calc(int now,int x){
	int res=inf;
	for(int i=now-3;i<=now+3;i++)res=min(res,max(i+x,a[1]-i));
	return res;
}
void mian(){
	cin>>n;
	for(int i=1;i<=n;i++)scanf("%lld",a+i);
	for(int i=1;i<n;i++)d[i]=a[i+1]-a[i];
	int x=0;
	for(int i=1;i<n;i++)if(d[i]>0)x+=d[i];
	cout<<calc(a[1]-x>>1,x)<<"\n";
	cin>>qu;
	while(qu--){
		int l,r,v;
		scanf("%lld%lld%lld",&l,&r,&v);
		if(l==1)a[1]+=v;
		if(l-1&&d[l-1]>0)x-=d[l-1];
		if(r<n&&d[r]>0)x-=d[r];
		d[l-1]+=v,d[r]-=v;
		if(l-1&&d[l-1]>0)x+=d[l-1];
		if(r<n&&d[r]>0)x+=d[r];
		printf("%lld\n",calc(a[1]-x>>1,x));
	}
}
signed main(){
	int testnum=1;
//	cin>>testnum;
	while(testnum--)mian();
	return 0;
}

E - Deleting Numbers

這是釣魚王子出的好題哦~

洛谷題目頁面傳送門 & CF 題目頁面傳送門

本題為交互題。給定 \(n\),初始時有 \(A=[1,n]\cap \mathbb Z\)。你有 \(3\) 種操作:

  1. 查詢 \(A\) 中有多少個 \(a\) 的倍數(\(a\in [1,n]\));
  2. 查詢 \(A\) 中有多少個 \(a\) 的倍數(\(a\in[2,n]\)),並將 \(A\) 中所有 \(a\) 的倍數刪去,特殊地,如果 \(x\)\(a\) 的倍數則不刪;
  3. 得出答案 \(a\),操作之后立即結束。

你需要在不超過 \(10^4\) 次操作內猜出某個預先設定好的 \(x\in A\)

\(n\in\left[2,10^5\right]\)

思路在於,通過操作 \(1\) 和操作 \(2\) 的配合,實現查詢 \(x\) 是否是 \(a\) 的倍數。

不難想到嘗試將 \(x\) 分解質因數,根據弱智的唯一分解定理可以確定 \(x\)

最 naive 的想法是每個質數的冪都試一遍。打個表發現 \(\pi(n)\) 大概是 \(9500+\),然后質數的冪大概是 \(9700\) 左右個。你可能會說,噫,好,這場 div. 2 我阿克了!哦上帝,瞧瞧這天真的聲音。注意到操作 \(2\) 是先返回結果再刪除的,也就是說它的返回結果是個幌子,你想知道 \([a\mid x]\) 必須要先 \(2\)\(1\)\(2\) 步走。

想到壽司晚宴那題的一個結論:將質數分成小質數和大質數,則每個數最多含有一個大質因子。

那么先將小質因子隨便毛搞搞,我們設 \(\tau(m)\) 表示 \(m\) 以內質數的冪的個數,那么是 \(2\tau(\sqrt n)\) 步的。實際上可以進一步優化,對於每個質數,先將它給 \(2\) 了,然后每個冪就可以直接查了。這樣是 \(\pi(\sqrt n)+\tau(\sqrt n)\) 的。別看這一步優化微不足道,其實她能決定你是否 AC。

現在把質因數分解式里的小質因子部分已經分解出來了,並且 \(A\) 里顯然只剩下 \(1\)\(x\) 和所有大質數。接下來的任務就是要找出 \(x\) 是否有大質因子,如果有的話是誰。

需要分出兩種情況:

  1. 小質因子部分 \(>1\)。那么顯然 \(x\) 是不屬於那個大質數集的。那還怕個鬼啊,直接檢查所有的大質數乘以小質因子部分是否剩一個數,剩的話大質因子就是他了乘上去,否則沒有;
  2. 小質因子部分 \(=1\)。此時就需要害怕了,因為你「檢查所有的大質數乘以小質因子部分是否剩一個數」的話,那你任何一次檢查結果都是「是」,就無語了。而此時問題變得更加簡單,剩下來的集合就是 \(1\) 和所有大質數,而你可以確定 \(x\) 就在里面。考慮對大質數序列分塊。每塊整體刪一下,然后如果集合大小減少數量不對勁就說明 \(x\) 一定在這塊里面,集中精力搞。由於塊大小不大,可以直接逐個用操作 \(1\) 排查。總操作次數大約為 \(\pi(\sqrt n)+\tau(\sqrt n)+(\pi(n)-\pi(\sqrt n))+2\sqrt{\pi(n)-\pi(\sqrt n)}=\tau(\sqrt n)+\pi(n)+2\sqrt{\pi(n)-\pi(\sqrt n)}\),卡的死死的,出題人真是毒瘤。
#include<bits/stdc++.h>
using namespace std;
#define pb push_back
bool ispr(int x){
	if(x<2)return false;
	for(int i=2;i*i<=x;i++)if(x%i==0)return false;
	return true;
}
int A(int x){
	printf("A %d\n",x),fflush(stdout);
	cin>>x;return x;
}
int B(int x){
	printf("B %d\n",x);fflush(stdout);
	cin>>x;return x;
}
void C(int x){printf("C %d\n",x);fflush(stdout);exit(0);}
void mian(){
	int n;
	cin>>n;
	if(n==1)C(1);
	vector<int> pr;
	for(int i=2;i<=n;i++)if(ispr(i))pr.pb(i);
	int x=1,lim=sqrt(n);
	for(int i=0;pr[i]<=lim;i++){
		B(pr[i]);
		int now=1;
		while(now*pr[i]<=n)now*=pr[i];
		while(now>1){
			if(A(now)==1){x*=now;break;}
			now/=pr[i];
		}
	}
	vector<int> v;
	for(int i=0;i<pr.size();i++)if(pr[i]>lim)v.pb(pr[i]);
	if(x==1){
		int now=A(1);
		for(int i=0;i<v.size();i+=100){
			for(int j=i;j<i+100&&j<v.size();j++)B(v[j]);
			int res=A(1);
			if(now-res==min(i+100,int(v.size()))-i)now=res;
			else{
				for(int j=i;j<i+100&&j<v.size();j++)if(A(v[j])==1){x=v[j];break;}
				break;
			}
		}
	}
	else{
		for(int i=0;i<v.size();i++)if(1ll*x*v[i]<=n&&A(x*v[i])==1){x*=v[i];break;}
	}
	C(x);
}
int main(){
	int testnum=1;
//	cin>>testnum;
	while(testnum--)mian();
	return 0;
}


免責聲明!

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



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