本文網址: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.
題目大意
思路
程式碼實現
尾聲
如果有什麼問題,可以直接評論!