CodeForces 1419 - Codeforces Round #671 (Div. 2)


蕪湖,終於把這場補完了,可以寫題解了(bushi


好了,現在我可以說我連 D2A 都不會了。如果 A 不法師塔可以 rank 10+ 然后把大號吊着錘來着。這 pretest 該噴,two pretests, hundreds of tests。

把上次的 2099 浪費掉了,奇跡果然辜負了奇跡啊(

接下來要打持久戰了,即每次用大小號中較遜的那個打,我乃有對於人類再生之確信(


CF 比賽頁面傳送門

A - Digit Game

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

有一個 \(n\) 位數,兩個人輪流操作。先手可以標記從左往右未標記的奇位數,后手可以標記未標記的偶位數。當僅剩一位未標記時,若它為奇數則先手勝,否則后手勝。判斷若兩人絕頂聰明,則誰勝。本題多測。

\(n\in[1,1000],T\in[1,100]\)

顯然,最終剩下來的那個位在誰手上是固定的。於是若那個人所掌控的所有位置中有它想要的奇偶性那么他就贏了,否則就輸了。

然鵝現場我把兩個人都看是否有奇數了,顯然是一奇一偶的。我真是個 sb:

wssbwssbwssbwssb

5448544854485448

#include<bits/stdc++.h>
using namespace std;
int n;
string a;
void mian(){
	cin>>n>>a;
	if(n&1){
		bool hav=false;
		for(int i=0;i<n;i+=2)hav|=(a[i]^48)%2==1;
		puts(hav?"1":"2");
	}
	else{
		bool hav=false;
		for(int i=1;i<n;i+=2)hav|=(a[i]^48)%2==0;
		puts(hav?"2":"1");
	}
}
int main(){
	int testnum=1;
	cin>>testnum;
	while(testnum--)mian();
	return 0;
}

B - Stairs

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

一個 \(x\) 級樓梯,有 \(x\) 層台階,第 \(i\) 層高 \(i\)(用 \(i\) 個磚塊)。稱 \(x\) 級樓梯是好的當且僅當它可以被恰好 \(x\) 個正方形所恰好覆蓋。給定 \(n\),求用不超過 \(n\) 個磚塊最多可以造多少個不同的好樓梯。本題多測。

\(n\in\!\left[1,10^{18}\right],T\in[1,1000]\)

隨便手玩玩可以發現結論:\(x\) 級樓梯是好的當且僅當 \(x\)\(2\) 的整次冪 \(-1\)。然后就很好做了,直接從小到大枚舉好樓梯級數,直到磚塊和超過 \(n\) 位置。

由於那些不證明結論的題解被噴了,然后題解區被清空了,這里證明一下該結論。

注意到每列的頂端(共 \(n\) 個)中任意兩個都不可能出現在同一個正方形中。於是若想要一個 \(x\) 級樓梯是好的,擺正方形方案一定是每個正方形各覆蓋一列的頂端。於是我們從左往右依次嘗試放置正方形。顯然到某一列的時候,如果現在不填滿該列,以后就沒機會填了,於是必須要填滿。於是選擇對應的邊長遞推下去,發現原命題是成立的。

#include<bits/stdc++.h>
using namespace std;
#define int long long
void mian(){
	int n;
	cin>>n;
	int now=0;
	for(int i=1;;i++){
		now+=((1ll<<i)-1)*(1ll<<i)/2;
		if(now>n)return cout<<i-1<<"\n",void();
	}
}
signed main(){
	int testnum=1;
	cin>>testnum;
	while(testnum--)mian();
	return 0;
}

C - Killjoy

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

\(n+1\) 個用戶(其中有一個 root),每個用戶有一個 rating。一開始只有 root 感染了新冠。可以進行若干次比賽(root 不參加),每次比賽用戶的 rating 會變化,唯一的要求是 rating 之和不變。初始時和每次比賽后所有患新冠的人都會傳染給所有與他同 rating 的人。問至少幾場比賽可以感染所有人。本題多測。

\(n\in[2,1000],T\in[1,100]\)

結論很顯然吧……

  • 如果一開始就全等於 root 的 rating 答案就是 \(0\)
  • 如果一開始至少有一個等於 root,則讓其他人都變成 root,一開始等於 root 的無所謂了,這樣顯然一定可以實現,答案為 \(1\)
  • 否則,若所有人 rating 平均數等於 root,則可以一次性全變成 root,否則需要 \(2\) 場。
#include<bits/stdc++.h>
using namespace std;
const int N=1000;
int n,x;
int a[N+1];
void mian(){
	cin>>n>>x;
	for(int i=1;i<=n;i++)scanf("%d",a+i);
	vector<int> v;
	for(int i=1;i<=n;i++)if(a[i]!=x)v.push_back(a[i]);
	if(v.empty())return puts("0"),void();
	if(v.size()<n)return puts("1"),void();
	int sum=0;
	for(int i=0;i<=n;i++)sum+=a[i];
	if(sum==x*n)return puts("1"),void();
	puts("2");
}
int main(){
	int testnum=1;
	cin>>testnum;
	while(testnum--)mian();
	return 0;
}

D1 / D2 - Sage's Birthday

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

\(n\) 個數 \(a_i\),你需要將它重新排列使得嚴格小於兩側的數的數量最大。輸出最大值以及排列方案。

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

答案顯然具有可二分性。對於一個答案,貪心地擺,從小到大從第二個格子隔一個擺,然后從大到小大的配大的看是否可行即可。

由於要排序,所以復雜度下界是線性對數。然后有些排序后線性的做法也沒有必要了,反正總復雜度跟我的二分答案是一樣的。

#include<bits/stdc++.h>
using namespace std;
#define mp make_pair
#define X first
#define Y second
const int N=100000;
int n;
int a[N+1];
pair<bool,vector<int> > sol(int x){
	bool flg=true;
	flg&=a[n]>a[x];
	for(int i=1;i<=x;i++)flg&=a[n-i]>a[x-i+1];
	if(!flg)return mp(false,vector<int>());
	vector<int> res(n);
	for(int i=1;i<=x;i++)res[2*(x-i+1)-1]=a[x-i+1],res[2*(x-i+1)-2]=a[n-i];
	res[2*x]=a[n];
	for(int i=2*x+1;i<n;i++)res[i]=a[x+i-2*x];
	return mp(true,res);
}
void mian(){
	cin>>n;
	for(int i=1;i<=n;i++)scanf("%d",a+i);
	sort(a+1,a+n+1);
	pair<int,vector<int> > ans;
	ans.X=0;
	for(int i=1;i<=n;i++)ans.Y.push_back(a[i]);
	for(int i=25;~i;i--)if(ans.X+(1<<i)<=n-1>>1){
		pair<bool,vector<int> > p=sol(ans.X+(1<<i));
		if(p.X)ans=mp(ans.X+(1<<i),p.Y);
	}
	cout<<ans.X<<"\n";
	for(int i=1;i<=n;i++)printf("%d ",ans.Y[i-1]);puts("");
}
int main(){
	int testnum=1;
//	cin>>testnum;
	while(testnum--)mian();
	return 0;
}

E - Decryption

AC 700 祭(

給一合數 \(n\),你需要將它的所有 \(>1\) 的因數排成一個環。問最少多少次在兩個數中間插入它們的最小公倍數,能使任意兩個相鄰的數不互質,並給出方案。本題多測。

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

顯然次數就是互質相鄰對的個數。我們要最小化這個個數。

注意到兩個數不互質當且僅當它們有共同的質因子。於是我們將 \(n\) 分解質因數。

然后考慮掃一遍所有的質因子,對於每個質因子只看此位置的后綴質因子可能組成的因數(這樣可以保證不重不漏地照顧到每個因數)。貪心的想,每一個質因子所對應的因數集合都要是連在一塊的,那么剩下來的就是不同質因子段之間的問題。我們想要答案最小化,於是希望盡可能地好好排列每個質因子段使得不同質因子段相鄰的地方是有公共質因子的。

實際上這個非常簡單。對於非首尾之間的相鄰,我們只需要在前面一段的最后放的是這兩個質因子的乘積即可。然后對於首尾的相鄰,只需要在第一段前面放的是首尾兩個質因子的乘積。這樣子答案就是 \(0\) 了。

不過這樣太理想化了。段數 \(=2\) 的時候需要特殊考慮,因為兩端之間相鄰了 \(2\) 次。當 \(2\) 個質因子次數都為 \(1\) 的時候,肉眼可見答案最小為 \(1\),只能處理掉 \(1\) 個相鄰;否則是可以的,你把次數 \(>1\) 的放前面來,兩個相鄰處分別是第一個質因子的 \(1,2\) 次方乘以第二個質因子即可。

然后這題就做完了。所以很多時候一些題要你構造啥方案最小化啥,其實都是個幌子,結論往往是答案只有幾種取值的。

#include<bits/stdc++.h>
using namespace std;
#define pb push_back
#define mp make_pair
#define X first
#define Y second
int gcd(int x,int y){return y?gcd(y,x%y):x;}
vector<pair<int,int> > v;
vector<int> now;
void dfs(int x,int num=1){
	if(x==v.size())return now.pb(num),void();
	for(int i=0;i<=v[x].X;i++,num*=v[x].Y){
		dfs(x+1,num);
	}
}
void mian(){
	int n;
	cin>>n;
	v.clear();
	for(int i=2;i*i<=n;i++)if(n%i==0){
		v.pb(mp(0,i));
		while(n%i==0)v.back().X++,n/=i;
	}
	if(n>1)v.pb(mp(1,n));
	sort(v.begin(),v.end());
	if(v.size()==2&&v[0].X==1&&v[1].X==1)return printf("%d %d %d\n%d\n",v[0].Y,v[1].Y,v[0].Y*v[1].Y,1),void();
	for(int i=0;i<v.size();i++){
		now.clear();
		dfs(i+1);
		vector<int> vv;
		for(int j=0;j<now.size();j++)for(int k=1,o=v[i].Y;k<=v[i].X;k++,o*=v[i].Y)vv.pb(now[j]*o);
		now=vv;
		for(int j=0;j<now.size();j++){
			if(i+1<v.size()&&now[j]==v[i].Y*v[i+1].Y)swap(now[j],now.back());
			if(i==0){
				if(v.size()==2&&now[j]==v[i].Y*v[1].Y*v[1].Y)swap(now[j],now[0]);
				if(v.size()>2&&now[j]==v[i].Y*v.back().Y)swap(now[j],now[0]);
			}
		}
		for(int j=0;j<now.size();j++)if(now[j])printf("%d ",now[j]);
	}
	puts("\n0");
}
int main(){
	int testnum=1;
	cin>>testnum;
	while(testnum--)mian();
	return 0;
}

F - Rain of Fire

這分明是個中等題嘛……不知道現場為啥沒啥人過,導致我直接放棄去逛 room 了,也不知道是如何評到 2800 的。

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

給定平面上 \(n\) 個點 \((x_i,y_i)\),你可以任選出發點,每次可以走到本行或本列距離不超過 \(t\) 的點,要求最終每個點至少走一遍。你可以隨意在空白位置放一個附加點(這個點也要走),或者不放,求最小 \(t\) 或報告無解。

\(n\in[2,1000],|x_i|,|y_i|\leq 10^9\)

首先顯而易見的,\(t\) 具有可二分性。於是我們二分答案,轉化為判定當前 \(t\) 是否可行。

注意到這個可以轉化為圖論模型:對於每個點,讓它和所有本行或本列距離不超過 \(t\) 的點連邊,然后可以加點或不加點使得圖連通就 ok。

注意到這里有一個很顯然並且很有用的建圖優化,即每個點只需要往上下左右四個方向各連最近的一個符合要求的點即可(沒有符合要求的則不連),而不需要真的連「所有」符合要求的點,這樣連通性顯然不變,並且每個點連邊數量是常數,總邊數線性。

那么接下來的任務是如何高效的判斷加點是否可以。考慮將加的點分為 \(2\) 類。

  1. 只在橫向或縱向上做橋梁。這種點的數量顯然是線性的,枚舉行 / 列,再枚舉本行 / 列所有相鄰點對即可;
  2. 兩個方向上都做橋梁的。這個看起來數量不好控制,其實不然。注意到它顯然要滿足它所在行有點且所在列有點,那么所在行和所在列的數量各是線性的(離散化預處理即可)。直接枚舉,是平方的。

然后考慮對於每個可能的加點的位置,如何判是否可行。顯然可以先 DFS 連通分解,然后對於每個點就暴力 merge 即可。可撤銷並查集的話是平方對數的,那么總復雜度就是平方二次對數,受不了。注意到每個點的 merge 量都是常數,所以完全可以直接暴力 set 或其他什么東西亂搞,看是否能把所有連通分量合並即可,隨便怎么搞只要跟 \(n\) 無關就是常數的。

還有一個需要思考的問題,就是第 \(2\) 類點如何快速找到它兩個方向上連的點對呢?sb 都會的是 lower_bound,可是那樣會炸。可以 two-pointers 控制到均攤常數。

那么現在一次 check 就控制到平方了。於是總復雜度平方對數。

然后各種筆誤 WA 了 2 發,wssb5448。

#include<bits/stdc++.h>
using namespace std;
#define int long long
#define mp make_pair
#define X first
#define Y second
#define pb push_back
const int N=1000;
int n; 
pair<int,int> a[N+1];
vector<int> nei[N+1];
vector<int> nums_x,nums_y;
void discrete(){
	sort(nums_x.begin(),nums_x.end());
	nums_x.resize(unique(nums_x.begin(),nums_x.end())-nums_x.begin());
	sort(nums_y.begin(),nums_y.end());
	nums_y.resize(unique(nums_y.begin(),nums_y.end())-nums_y.begin());
}
int cnt,cid[N+1];
void dfs(int x){
	cid[x]=cnt;
	for(int i=0;i<nei[x].size();i++){
		int y=nei[x][i];
		if(!cid[y])dfs(y);
	}
}
vector<pair<int,int> > row[N],col[N];
int now_r[N],now_c[N];
bool chk(int x){
	for(int i=1;i<=n;i++)nei[i].clear();
	for(int i=0;i<nums_x.size();i++){
		for(int j=0;j+1<row[i].size();j++)
			if(abs(row[i][j].X-row[i][j+1].X)<=x)
				nei[row[i][j].Y].pb(row[i][j+1].Y),nei[row[i][j+1].Y].pb(row[i][j].Y);
	}
	for(int i=0;i<nums_y.size();i++){
		for(int j=0;j+1<col[i].size();j++)
			if(abs(col[i][j].X-col[i][j+1].X)<=x)
				nei[col[i][j].Y].pb(col[i][j+1].Y),nei[col[i][j+1].Y].pb(col[i][j].Y);
	}
	cnt=0,memset(cid,0,sizeof(cid));
	for(int i=1;i<=n;i++)if(!cid[i])cnt++,dfs(i);
	if(cnt==1)return true;
	if(cnt<=2){
		for(int i=0;i<nums_x.size();i++){
			for(int j=0;j+1<row[i].size();j++)
				if(cid[row[i][j].Y]!=cid[row[i][j+1].Y]&&abs(row[i][j].X-row[i][j+1].X)+1>>1<=x)return true;
		}
		for(int i=0;i<nums_y.size();i++){
			for(int j=0;j+1<col[i].size();j++)
				if(cid[col[i][j].Y]!=cid[col[i][j+1].Y]&&abs(col[i][j].X-col[i][j+1].X)+1>>1<=x)return true;
		}
	}
	memset(now_r,0,sizeof(now_r)),memset(now_c,0,sizeof(now_c));
	if(cnt<=4)for(int i=0;i<nums_x.size();i++)for(int j=0;j<nums_y.size();j++){
		int xx=nums_x[i],yy=nums_y[j];
		while(now_r[i]<row[i].size()&&row[i][now_r[i]].X<yy)now_r[i]++;
		while(now_c[j]<col[j].size()&&col[j][now_c[j]].X<xx)now_c[j]++;
		if(now_r[i]<row[i].size()&&row[i][now_r[i]].X==yy)continue;
		set<int> st;
		if(now_r[i]&&abs(row[i][now_r[i]-1].X-yy)<=x)st.insert(cid[row[i][now_r[i]-1].Y]);
		if(now_r[i]<row[i].size()&&abs(row[i][now_r[i]].X-yy)<=x)st.insert(cid[row[i][now_r[i]].Y]);
		if(now_c[j]&&abs(col[j][now_c[j]-1].X-xx)<=x)st.insert(cid[col[j][now_c[j]-1].Y]);
		if(now_c[j]<col[j].size()&&abs(col[j][now_c[j]].X-xx)<=x)st.insert(cid[col[j][now_c[j]].Y]);
		if(st.size()==cnt&&*st.begin()==1&&*--st.end()==cnt)return true;
	}
	return false;
}
signed main(){
	cin>>n;
	for(int i=1;i<=n;i++)cin>>a[i].X>>a[i].Y,nums_x.pb(a[i].X),nums_y.pb(a[i].Y);
	discrete();
	for(int i=1;i<=n;i++){
		row[lower_bound(nums_x.begin(),nums_x.end(),a[i].X)-nums_x.begin()].pb(mp(a[i].Y,i));
		col[lower_bound(nums_y.begin(),nums_y.end(),a[i].Y)-nums_y.begin()].pb(mp(a[i].X,i));
	}
	for(int i=0;i<nums_x.size();i++)sort(row[i].begin(),row[i].end());
	for(int i=0;i<nums_y.size();i++)sort(col[i].begin(),col[i].end());
	int ans=4e9;
	for(int i=32;~i;i--)if(ans-(1ll<<i)>=0&&chk(ans-(1ll<<i)))ans-=1ll<<i;
	cout<<(ans==4e9?-1:ans);
	return 0;
}


免責聲明!

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



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