Codeforces Round 951 (Div. 2) 題解

zsc985246發表於2024-06-07

本文網址:https://www.cnblogs.com/zsc985246/p/18236377 ,轉載請註明出處。

好久沒更新了,詐屍一下。

挑戰最快題解。

B 題剛做完的時候網路爆炸了,掉了一點分。最終排名 21。

題目做起來很爽。大愛思維與構造!抵制資料結構!

F 題解請等待後續更新。

傳送門

Codeforces Round 951 (Div. 2)

A.Guess the Maximum

題目大意

給定一個長度為 \(n\) 的陣列 \(a\),求一個最大的數 \(x\),使得任意一個長度不小於 \(2\) 的連續子串的最大值大於 \(x\)

多組測試,\(2 \le n \le 5 \times 10^4, 1 \le a_i \le 10^9, T \le 10^4, \sum n \le 5 \times 10^4\)

思路

列舉所有長度為 \(2\) 的連續子串,求出它們最大值的最小值,然後減去 \(1\) 輸出即可。

程式碼實現

#include<bits/stdc++.h>
#define ll long long
#define For(i,a,b) for(ll i=(a);i<=(b);++i)
#define Rep(i,a,b) for(ll i=(a);i>=(b);--i)
const ll N=1e6+10;
using namespace std;

ll n,m,k;
ll a[N],b[N];

void mian(){
	
	ll ans=1e9;
	scanf("%lld",&n);
	For(i,1,n){
		scanf("%lld",&a[i]);
		if(i>1)ans=min(ans,max(a[i],a[i-1]));
	}
	
	printf("%lld\n",ans-1);
	
}

int main(){
	int T=1;
	scanf("%d",&T);
	while(T--)mian();
	return 0;
}

B.XOR Sequences

題目大意

給定兩個數 \(x,y\),構造無限長度序列 \(a_n = n \oplus x , b_n = n \oplus y\)。求兩個序列的最長公共子序列長度。

多組測試,\(0 \le x,y \le 10^9, x \neq y, T \le 10^4\)

思路

考慮公共子序列如何形成。

假設現在滿足 \(x \oplus n = y \oplus n\)。如果想要 \(n\) 自增之後仍然滿足條件,那麼在自增過程中,\(n\) 的每一個發生了改變的二進位制位,在 \(x,y\) 的對應位都必須相等。

,令 \(x,y\) 從最低位開始的連續相同二進位制位的個數為 \(t\),那麼答案即為 \(2^t\)

程式碼實現

#include<bits/stdc++.h>
#define ll long long
#define For(i,a,b) for(ll i=(a);i<=(b);++i)
#define Rep(i,a,b) for(ll i=(a);i>=(b);--i)
const ll N=1e6+10;
using namespace std;

ll n,m,k;
ll a[N],b[N];

void mian(){
	
	ll ans=0;
	scanf("%lld%lld",&n,&m);
	
	while(((n>>ans)&1)==((m>>ans)&1))++ans;
	
	printf("%lld\n",1ll<<ans);
	
}

int main(){
	int T=1;
	scanf("%d",&T);
	while(T--)mian();
	return 0;
}

C.Earning on Bets

題目大意

給定一個長度為 \(n\) 的序列 \(a\),你需要構造一個長度也為 \(n\) 的序列 \(b\),滿足 \(\forall i,a_i b_i > \sum_{j=1}^{n} b_j\)

無解輸出 \(-1\)

多組測試,\(1 \le n \le 50, 2 \le a_i \le 20, T \le 10^4, \sum n \le 2 \times 10^5\)

思路

顯然讓所有的 \(a_i b_i\) 都相等時最優。

求出 \(a_i\) 的最大公倍數,然後計算驗證是否合法即可。

程式碼實現

#include<bits/stdc++.h>
#define ll long long
#define For(i,a,b) for(ll i=(a);i<=(b);++i)
#define Rep(i,a,b) for(ll i=(a);i>=(b);--i)
const ll N=1e6+10;
using namespace std;

ll n,m,k;
ll a[N],b[N];

void mian(){
	
	ll ans=0;
	scanf("%lld",&n);
	For(i,1,n){
		scanf("%lld",&a[i]);
		if(i==1)ans=a[i];
		else ans=ans/__gcd(ans,a[i])*a[i];
	}
	
	ll s=0;
	For(i,1,n){
		b[i]=ans/a[i];
		s+=b[i];
	}
	
	if(ans<=s){
		printf("-1\n");
		return;
	}
	For(i,1,n){
		printf("%lld ",b[i]);
	}
	printf("\n");
	
}

int main(){
	int T=1;
	scanf("%d",&T);
	while(T--)mian();
	return 0;
}

D.Fixing a Binary String

題目大意

給定一個長度為 \(n\) 的 01 串 \(s\) 和一個整數 \(k\),你需要恰好進行一次操作:

  • 選擇一個 \(1 \le p \le n\),將序列變為 \(s_{p+1} s_{p+2} \dots s_{n} s_p s_{p-1} \dots s_1\)

定義一個 01 串 \(s\) 是好的當且僅當滿足以下條件:

  • \(s_1=s_2=\dots=s_k\)

  • \(\forall 1 \le i \le n-k,s_i \neq s_{i+k}\)

多組測試,\(2 \le n \le 10^5, 1 \le k \le n, T \le 10^4, \sum n \le 2 \times 10^5\)\(n\)\(k\) 的倍數

思路

發現操作後 \(s_1\) 總在最後,結合序列的最終條件,考慮從這裡入手。

為了滿足序列的格式,\(s_1\)\(s_{\lceil\frac{p}{k}\rceil \times k}\) 以及 \(s_{p+1}\)\(s_{\lceil\frac{n-p}{k}\rceil \times k+p}\) 必定是好的 01 串。

而我們需要做的就是將中間的一段拼接起來。

分類討論即可。具體過程見程式碼。

程式碼實現

注意細節。

#include<bits/stdc++.h>
#define ll long long
#define For(i,a,b) for(ll i=(a);i<=(b);++i)
#define Rep(i,a,b) for(ll i=(a);i>=(b);--i)
const ll N=1e6+10;
using namespace std;

ll n,m,k;
ll a[N],b[N];

ll check(ll ans){//驗證b陣列是否是好的
	ll t=b[1],cnt=1;
	For(i,2,n){
		if(b[i]==t&&cnt<m)++cnt;
		else if(b[i]!=t&&cnt==m)t=b[i],cnt=1;
		else return -1;
	}
	return ans;
}

void mian(){
	
	ll ans=0;
	scanf("%lld%lld",&n,&m);
	For(i,1,n){
		scanf("%1lld",&a[i]);
	}
	
	ll t=a[1],cnt=1,tmp=0;//t為當前數,cnt為連續出現次數,tmp為第一個不滿足好串的點
	For(i,2,n){
		if(a[i]==t&&cnt<m)++cnt;
		else if(a[i]!=t&&cnt==m)t=a[i],cnt=1;
		else{
			tmp=i;
			break;
		}
	}
	if(tmp==0){//原串就是好的
		printf("%lld\n",n);
		return;
	}
	if(cnt!=m||cnt==m&&a[tmp-1]!=a[n]){
		//只能選擇p=tmp-1的情況
		//cnt!=m時,p後移會導致當前數過少
		//cnt==m且a[tmp-1]!=a[n]時,p前移導致當前數過少,後移導致當前數過多
		For(i,tmp,n)b[i-tmp+1]=a[i];
		Rep(i,tmp-1,1)b[n-i+1]=a[i];
		printf("%lld\n",check(tmp-1));
		return;
	}
	ll tot=0;//統計結尾的連續長度
	Rep(i,n,1){
		if(a[i]==a[n])++tot;
		else break;
	}
	if(tot>m){//已經超過了要求數量
		printf("-1\n");
		return;
	}
	tmp-=tot;//用m-tot個與結尾拼合
	For(i,tmp,n)b[i-tmp+1]=a[i];
	Rep(i,tmp-1,1)b[n-i+1]=a[i];
	printf("%lld\n",check(tmp-1));
	
}

int main(){
	int T=1;
	scanf("%d",&T);
	while(T--)mian();
	return 0;
}

E.

題目大意

對於兩個點 \((x_1,y_1),(x_2,y_2)\),曼哈頓距離為 \(|x_1-x_2|+|y_1-y_2|\)

給定平面上 \(n\) 個點 \((x_i,y_i)\) 和一個整數 \(k\)。定義三個點形成好的三角形當且僅當任意兩點的曼哈頓距離都為 \(k\)

求出給定的點中的一個好的三角形。若存在,輸出三個點的編號;若不存在,輸出三個 \(0\)

多組測試,\(3 \le n \le 10^5, 2 \le k \le 4 \times 10^5, -10^5 \le x_i,y_i \le 10^5, T \le 10^4, \sum n \le 2 \times 10^5\)\(k\) 是偶數

思路

曼哈頓距離有一個特點:到一個點的曼哈頓距離相同的點組成一個菱形。

根據這個特點可以發現,一個好的三角形,至少有一條過兩個點的直線傾斜角為 \(45^\circ\)\(135^\circ\)

我們不妨列舉這條直線,從而確定兩個點。

傾斜角 \(45^\circ\)\(135^\circ\) 的情況可以分開考慮,只需要讓所有點關於 \(y\) 軸對稱過去即可。

將處於同一條直線的點用 vector 記錄下來,並按照 \(x\) 座標排好序。

雙指標找到曼哈頓距離為 \(k\) 的直線上的兩點,然後在離這條直線水平距離為 \(k\) 的兩條直線上二分找到三角形的第三個點。

複雜度 \(O(n \log n)\)

程式碼實現

注意下標加上一個較大的數防止變成負數。

#include<bits/stdc++.h>
#define ll long long
#define For(i,a,b) for(ll i=(a);i<=(b);++i)
#define Rep(i,a,b) for(ll i=(a);i>=(b);--i)
#define pb push_back
const ll N=1e6+10;
using namespace std;

ll BIG=300000;//一個較大的數
ll n,k;
ll a[N],b[N];
ll m,f[N];
vector<ll>t[N];
struct node{
	ll a,b,c;
}ans;

bool cmp(ll x,ll y){
	return a[x]<a[y];
}

ll find(ll pos,ll x,ll y){//二分
	if(pos<100000||pos>500000)return 0;//防止下標溢位
	ll l=0,r=(ll)t[pos].size()-1,res=0;
	while(l<=r){
		ll mid=(l+r)>>1;
		if(a[t[pos][mid]]>=x)res=t[pos][mid],r=mid-1;
		else l=mid+1;
	}
	if(res&&a[res]<=y)return res;
	else return 0;
}

void calc(){
	m=0;
	For(i,1,n){
		f[++m]=a[i]-b[i]+BIG;
		t[a[i]-b[i]+BIG].pb(i);
	}
	//離散化
	sort(f+1,f+m+1);
	m=unique(f+1,f+m+1)-f-1;
	
	For(i,1,m)sort(t[f[i]].begin(),t[f[i]].end(),cmp);//按x座標排序
	
	For(i,1,m){
		ll y=0;
		For(x,0,(ll)t[f[i]].size()-1){
			while(y<(ll)t[f[i]].size()&&a[t[f[i]][y]]-a[t[f[i]][x]]<k/2)++y;
			if(y>=(ll)t[f[i]].size())break;
			if(a[t[f[i]][y]]-a[t[f[i]][x]]==k/2){
				ll t1=find(f[i]-k,a[t[f[i]][x]]*2-a[t[f[i]][y]],a[t[f[i]][x]]);
				ll t2=find(f[i]+k,a[t[f[i]][y]],a[t[f[i]][y]]*2-a[t[f[i]][x]]);
				if(t1)ans=(node){t[f[i]][x],t[f[i]][y],t1};
				if(t2)ans=(node){t[f[i]][x],t[f[i]][y],t2};
			}
		}
	}
	
	For(i,1,m)t[f[i]].clear();
}

void mian(){
	
	ans=(node){0,0,0};
	scanf("%lld%lld",&n,&k);
	For(i,1,n){
		scanf("%lld%lld",&a[i],&b[i]);
	}
	
	calc();
	For(i,1,n)a[i]=-a[i];
	calc();
	
	printf("%lld %lld %lld\n",ans.a,ans.b,ans.c);
	
}

int main(){
	int T=1;
	scanf("%d",&T);
	while(T--)mian();
	return 0;
}

F.

題目大意

思路

程式碼實現


尾聲

如果有什麼問題,可以直接評論!

相關文章