总结
\(t1\) 看起来好简单啊,\(5min\) 切了。
\(t2\) 看起来好简单啊,\(15min\) 切了。
\(t3\) 看起来好难啊,\(50min\) 跳了。
一开始想到了二分答案,迟迟想不到 \(O(n)\) 的 \(check\),于是思维开始发散,从贪心想到 \(dp\)。
考完发现大家都是 \(O(n^2)\) 的 \(check\),wssb。
\(t4\) 是道交互,直接跳了。
\(t5\) 看起来还行,让我做做。
\(after\ 1\ hour......\)
还是没想出来,还差一个 \(dp\) 就想出来了,但就是抓不到方程。。。
比赛结束前 \(5min\) 想到了做法,可惜大势已去。。。
只做出来两道题,太惨了。
实力太菜导致的。
(考后两分钟交了 \(t5\) 并 \(AC\))
解析
难度:红
简单题。
#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;
}
难度:橙
如果不是 \(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;
}
难度:黄-绿
想着用二分,结果 \(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;
}
难度:绿
发掘一下最大值的性质!如果最大值位置为 \(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;
}
难度:绿
题目转化一下:在以 \(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;
}
难度:紫
我们发现,求出时间复杂度的瓶颈在于:
把儿子大小构成的数集合分成差尽可能小的两部分。
对于这里的优化,有很多做法,如 \(FFT\),\(bitset\),这里选择 \(bitset\) 的做法。
首先,若 \(sze_v\geq\frac{1}{2}sze_u\),直接返回 \(sze_v*(sze_u-sze_v-1)\) 就行了,这里不解释。
剩下的就是一个二进制优化完全背包,由于时限和动态大小,所以这里要手写 \(bitset\)。
懒得写了,抛个soluion代码。
#include<bits/stdc++.h>
#define ll long long
#define mxn 1000010
using namespace std;
int n;
vector<int> e[mxn];
int siz[mxn];
int cnt[mxn];
unsigned ll vis[(mxn>>6)+10];
void lshift(int n,int x){
if((x&63)==0){
for(int i=n;i>=(x>>6);--i)
vis[i]|=vis[i-(x>>6)];
}
else{
for(int i=n;i>=(x>>6);--i){
int wzl=(i<<6)-x;
vis[i]|=(wzl<0?0:vis[wzl>>6]>>(wzl&63))|(vis[(wzl>>6)+1]<<(64-(wzl&63)));
}
}
}
ll ans=0;
void dfs(int u){
siz[u]=1;
int ax=0;
for(int v:e[u]){
dfs(v);
siz[u]+=siz[v];
ax=max(ax,siz[v]);
}
if(ax*2>=siz[u]-1)
ans+=1ll*ax*(siz[u]-1-ax);
else{
for(int v:e[u])
++cnt[siz[v]];
memset(vis,0,((siz[u]-1>>6)+5)*8);
vis[0]=1;
for(int v:e[u]){
if(cnt[siz[v]]==0)continue;
int now=1;
while(now<=cnt[siz[v]]){
lshift(siz[u]-1>>6,siz[v]*now);
cnt[siz[v]]-=now,now<<=1;
}
if(cnt[siz[v]]!=0)
lshift(siz[u]-1>>6,siz[v]*cnt[siz[v]]);
cnt[siz[v]]=0;
}
int mid=(siz[u]-1)/2;
for(int i=0;;++i){
if(vis[mid+i>>6]>>(mid+i&63)&1){
ans+=1ll*(mid+i)*(siz[u]-1-mid-i);
break;
}
if(vis[mid-i>>6]>>(mid-i&63)&1){
ans+=1ll*(mid-i)*(siz[u]-1-mid+i);
break;
}
}
}
}
int main(){
ios::sync_with_stdio(0);
cin.tie(0),cout.tie(0);
cin>>n;
for(int i=2;i<=n;++i){
int f;cin>>f;
e[f].push_back(i);
}
dfs(1);
cout<<ans;
return 0;
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 单线程的Redis速度为什么快?
· 展开说说关于C#中ORM框架的用法!
· Pantheons:用 TypeScript 打造主流大模型对话的一站式集成库
· SQL Server 2025 AI相关能力初探
· 为什么 退出登录 或 修改密码 无法使 token 失效