总结
\(t1\) 看起来好简单啊,\(5min\) 切了。
\(t2\) 看起来好简单啊,\(15min\) 切了。
\(t3\) 看起来好难啊,\(50min\) 跳了。
一开始想到了二分答案,迟迟想不到 \(O(n)\) 的 \(check\),于是思维开始发散,从贪心想到 \(dp\)。
考完发现大家都是 \(O(n^2)\) 的 \(check\),wssb。
\(t4\) 是道交互,直接跳了。
\(t5\) 看起来还行,让我做做。
\(after\ 1\ hour......\)
还是没想出来,还差一个 \(dp\) 就想出来了,但就是抓不到方程。。。
比赛结束前 \(5min\) 想到了做法,可惜大势已去。。。
只做出来两道题,太惨了。
实力太菜导致的。
(考后两分钟交了 \(t5\) 并 \(AC\))
解析
A. Tales of a Sort
难度:红
简单题。
#include<bits/stdc++.h>
#define mxn 55
#define ll long long
using namespace std;
ll t,n,a[mxn],ans;
void solve(){
cin>>n;ans=0;
for(int i=1;i<=n;i++)cin>>a[i];
for(int i=1;i<n;i++)
if(a[i]>a[i+1])ans=max(ans,a[i]);
cout<<ans<<'\n';
}
int main(){
ios::sync_with_stdio(0);
cin.tie(0),cout.tie(0);
cin>>t;while(t--)solve();
return 0;
}
B. Good Arrays
难度:橙
如果不是 \(1\) 对其他数的贡献为 \(a_i-1\),是 \(1\) 贡献为 \(-1\)。
最后判断贡献是否 \(<0\) 就行了。
#include<bits/stdc++.h>
#define mxn 100010
#define ll long long
using namespace std;
ll t,n,a[mxn],sum;
void solve(){
cin>>n;sum=0;
for(int i=1;i<=n;i++)cin>>a[i];
if(n==1){cout<<"NO\n";return;}
for(int i=1;i<=n;i++){
if(a[i]==1)sum--;
else sum+=a[i]-1;
}
if(sum<0)cout<<"NO\n";
else cout<<"YES\n";
}
int main(){
ios::sync_with_stdio(0);
cin.tie(0),cout.tie(0);
cin>>t;while(t--)solve();
return 0;
}
C. To Become Max
难度:黄-绿
想着用二分,结果 \(O(n)\) 的 \(check\),结果很久都没写出来,玉玉了。
打完才发现 \(check\) 是可以 \(O(n^2)\) 的。
#include<bits/stdc++.h>
#define mxn 1010
#define ll long long
using namespace std;
ll n,k,t,a[mxn];
ll l,r,ans;
ll tot,b[mxn];
bool flg=1;
bool check(ll x){
if(a[n]>=x)return 1;
for(int i=1;i<n;i++){
if(a[i]>=x)return 1;
tot=k,flg=1;
for(int j=1;j<=n;j++)b[j]=a[j];
tot-=x-b[i],b[i]=x;
if(tot<0)continue;
for(int j=i+1;j<n;j++){
if(b[j]>=b[j-1]-1)return 1;
tot-=(b[j-1]-1)-b[j];
b[j]=b[j-1]-1;
if(tot<0){
flg=0;break;
}
}
if(flg&&b[n]>=b[n-1]-1)return 1;
}
return 0;
}
void solve(){
cin>>n>>k;
for(int i=1;i<=n;i++)cin>>a[i];
l=1,r=1e12;
while(l<=r){
ll mid=(l+r)>>1;
if(check(mid))ans=mid,l=mid+1;
else r=mid-1;
}
cout<<ans<<'\n';
}
int main(){
ios::sync_with_stdio(0);
cin.tie(0),cout.tie(0);
cin>>t;while(t--)solve();
return 0;
}
D. More Wrong
难度:绿
发掘一下最大值的性质!如果最大值位置为 \(p\),则有 \(ask(l,p)=ask(l,p-1)\)。
因为找不到比 \(a_p\) 更大的数了。
所以可以分治查找。复杂度 \(O(4n^2)\)。
#include<bits/stdc++.h>
#define mxn 5010
#define pb push_back
using namespace std;
int n,t;
int ask(int l,int r){
cout<<"? "<<l<<' '<<r<<endl;
int ret;cin>>ret;return ret;
}
int dfs(int l,int r){
if(l==r)return l;
if(l==r-1)return ask(l,r)?l:r;
int mid=(l+r)>>1,x=dfs(l,mid),y=dfs(mid+1,r);
if(ask(l,y-1)==ask(l,y))return y;
else return x;
}
void solve(){
cin>>n;
int ans=dfs(1,n);
cout<<"! "<<ans<<endl;
}
int main(){
ios::sync_with_stdio(0);
cin.tie(0),cout.tie(0);
cin>>t;while(t--)solve();
return 0;
}
E1. PermuTree (easy version)
难度:绿
题目转化一下:在以 \(i\) 为根的树下选取若干子树分成两部分,使子树大小的和的乘积最大。
数据较小的时候直接暴力背包就行了。
#include<bits/stdc++.h>
#define mxn 5010
#define pb push_back
#define ll long long
using namespace std;
ll n,k,a[mxn];
ll dp[mxn],sze[mxn],ans;
vector<int> t[mxn];
void solve(){
cin>>n>>k;
for(int i=1;i<=n;i++)cin>>a[i];
}
void dfs1(int u,int f){
sze[u]=1;
for(int i=0;i<t[u].size();i++){
int v=t[u][i];
if(v==f)continue;
dfs1(v,u);
sze[u]+=sze[v];
}
return;
}
void dfs2(int u,int f){
ll cnt=0,sum=0,maxn=0,vis[mxn]={0};
vis[0]=1;
for(int i=0;i<t[u].size();i++){
int v=t[u][i];
if(v==f)continue;
dfs2(v,u);
for(int j=sze[u];j>=0;j--)
if(vis[j]){
vis[j+sze[v]]=1;
maxn=max(maxn,(j+sze[v])*(sze[u]-1-j-sze[v]));
}
}
ans+=maxn;
return;
}
int main(){
ios::sync_with_stdio(0);
cin.tie(0),cout.tie(0);
cin>>n;
for(int i=1+1;i<=n;i++){
int v;cin>>v;
t[i].pb(v),t[v].pb(i);
}
dfs1(1,0);
dfs2(1,0);
cout<<ans;
return 0;
}
E2. PermuTree (hard version)
难度:紫
我们发现,求出时间复杂度的瓶颈在于:
把儿子大小构成的数集合分成差尽可能小的两部分。
对于这里的优化,有很多做法,如 \(FTT\),\(bitset\),这里选择 \(bitset\) 的做法。
代码先咕着。