Codeforces Round 987 (Div. 2) 總結
A
常見的套路,將一個序列變為不下降序列所需要改變的值的最小數量,考慮最大能保留多少個,顯然是求最長上升子序列,而這題給出的 \(a\) 序列保證不上升,所以只需要考慮相同長度的一段。
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cmath>
#include <vector>
#include <queue>
#include <map>
using namespace std;
typedef long long ll;
const int N=55;
int n;
int a[N];
void solve()
{
cin>>n;
for(int i=1;i<=n;i++) cin>>a[i];
int ans=0,len=0;
for(int i=1;i<=n;i++)
{
if(a[i]!=a[i-1]) ans=max(ans,len),len=1;
else len++;
}
ans=max(ans,len);
cout<<n-ans<<'\n';
}
int main ()
{
#ifndef ONLINE_JUDGE
freopen("1.in","r",stdin);
freopen("1.out","w",stdout);
#endif
ios::sync_with_stdio(0);
cin.tie(0);cout.tie(0);
int T;
cin>>T;
while(T--) solve();
return 0;
}
B
考慮每個數 \(p_i\) 能否移動到 \(i\) 位置。
首先能交換的值只有 \(p_i-1\) 和 \(p_i+1\),顯然不能連續移動兩次,不然比 \(p_i\) 大或小 \(1\) 的數一定不會到該到的位置。因此最多交換一次。再看是否能交換到自己想要的位置,如果有一個不能,那就不可行。
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cmath>
#include <vector>
#include <queue>
#include <map>
using namespace std;
typedef long long ll;
const int N=2e5+5;
int n;
int a[N];
void solve()
{
cin>>n;
for(int i=1;i<=n;i++) cin>>a[i];
int st=1;
for(int i=1;i<=n;i++)
{
if(a[i]<i&&!(i-a[i]==1&&a[a[i]]==a[i]+1)) st=0;
else if(a[i]>i&&!(a[i]-i==1&&a[a[i]]==a[i]-1)) st=0;
}
if(st) cout<<"Yes\n";
else cout<<"No\n";
}
int main ()
{
#ifndef ONLINE_JUDGE
freopen("1.in","r",stdin);
freopen("1.out","w",stdout);
#endif
ios::sync_with_stdio(0);
cin.tie(0);cout.tie(0);
int T;
cin>>T;
while(T--) solve();
return 0;
}
C
構造題,思路還是比較清晰的。
首先不難想到 \(1,1,2,2,3,3,\dots\) 這樣的情況,所以 \(n\) 為偶數時一定成立。
再考慮奇數,由於任意兩個相同的餡料之間的距離都要是完全平方數,考慮三個相同的,位置為 \(x,y,z\),滿足 \(y-x=a^2\),\(z-y=b^2\),\(z-x=c^2\),且 \(a,b,c\) 都為正整數,因此有 \(c^2=a^2+b^2\)。
考慮最小的勾股數 \(3,4,5\)。令 \(x=1,y=10,z=26\),這樣剩下的位置就是偶數個,比較好構造了。下面給出一種構造方案:
後面就按偶數的接下去就行了。
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cmath>
#include <vector>
#include <queue>
#include <map>
using namespace std;
typedef long long ll;
const int N=2e5+5;
int n;
int a[N];
void solve()
{
cin>>n;
if(n%2==0)
{
int cnt=1;
for(int i=1;i<=n;i+=2) a[i]=a[i+1]=++cnt;
}
else
{
if(n>=27)
{
a[1]=a[10]=a[26]=1;
int cnt=1;
for(int i=2;i<=8;i+=2) a[i]=a[i+1]=++cnt;
for(int i=11;i<=21;i+=2) a[i]=a[i+1]=++cnt;
a[23]=a[27]=++cnt;
a[24]=a[25]=++cnt;
for(int i=28;i<=n;i+=2) a[i]=a[i+1]=++cnt;
}
else
{
cout<<-1<<'\n';
return ;
}
}
for(int i=1;i<=n;i++) cout<<a[i]<<' ';
cout<<'\n';
}
int main ()
{
#ifndef ONLINE_JUDGE
freopen("1.in","r",stdin);
freopen("1.out","w",stdout);
#endif
ios::sync_with_stdio(0);
cin.tie(0);cout.tie(0);
int T;
cin>>T;
while(T--) solve();
return 0;
}
D
賽時的清奇想法。
首先發現能跳躍的兩個位置是逆序對,因此考慮用並查集維護,並記錄集合內最大值與最小值。
再考慮這樣一種做法,先遍歷一遍陣列,目前遇到的最大值為 \(x\),下標為 \(id\),加入一個數 \(a_i\)。
- 若 \(a_i>=x\) 更新 \(x\) 和 \(id\)。
- 若 \(a_i<x\) 那麼就合併 \(i\) 和 \(id\)。
以最後一組樣例為例:
用 \(mx_i\) 表示集合 \(i\) 中最大的 \(a_i\),\(mi_i\) 表示最小值。
然後考慮合併不同集合,從後往前,如果出現兩個不同集合 \(i,j\) 且 \(i<j\)。由前面的過程易知 \(mx_i \le mx_j\),如果 \(mx_i>mi_j\),就說明這兩個集合可以合併。
那有沒有可能出現不是相鄰的集合合併呢?答案是否定的,考慮 \(k,i,j\) 三個集合,\(mx_k \le mx_i \le mx_j\),如果 \(i\) 與 \(j\) 不能合併,則 \(mx_i \le mi_j\),就會有 \(mx_k \le mx_i \le mi_j\),顯然 \(k\) 與 \(j\) 不能合併。因此每個集合都只能和相鄰的集合合併。
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cmath>
#include <vector>
#include <queue>
#include <map>
using namespace std;
typedef long long ll;
const int N=5e5+5;
int n;
int a[N],ans[N];
int fa[N],mi[N],mx[N];
int find(int x)
{
if(x==fa[x]) return x;
return fa[x]=find(fa[x]);
}
void merge(int x,int y)
{
x=fa[x],y=fa[y];
fa[y]=x;
mi[x]=min(mi[x],mi[y]);
mx[x]=max(mx[x],mx[y]);
}
void solve()
{
cin>>n;
for(int i=1;i<=n;i++)
{
cin>>a[i];
fa[i]=i;
mi[i]=mx[i]=a[i];
}
int x=a[1],id=1;
for(int i=2;i<=n;i++)
{
if(a[i]<x) merge(id,i);
else x=a[i],id=i;
}
for(int i=n-1;i>=1;i--)
if(find(i)!=find(i+1)&&mi[find(i+1)]<mx[find(i)])
merge(i,i+1);
for(int i=1;i<=n;i++) cout<<mx[find(i)]<<' ';
cout<<'\n';
}
int main ()
{
#ifndef ONLINE_JUDGE
freopen("1.in","r",stdin);
freopen("1.out","w",stdout);
#endif
ios::sync_with_stdio(0);
cin.tie(0);cout.tie(0);
int T;
cin>>T;
while(T--) solve();
return 0;
}