筑基赛Day 2总结及题目简单梳理
今天大概是8:45开始写,写了三个多小时。
写的时候还是有说话的问题(但今天也没说个什么)。
先开的T1,一开始还把题看错了,然后很快看到问题,大概花了20-30min才写出来。然后看到T2,发现以前写过但是没写出来,还是有一点情绪在里面的。没看T3,其实应该好好读一下题的,因为50分的做法真的很显然。然后看T4,信誓旦旦的写了,结果最后发现求逆元太暴力,全部爆零了。主要时间在肝T2,想过很多做法,但是就是没有想过去进行操作使得可以
总的来说,还是不应该畏惧难题,有可能它只是看起来比较难,也有可能它的部分分其实非常好写。
T1 Haybale Feast G
这道题非常一眼,看到求辣度的最值我们可以很显然想到用稀疏表来维护区间最值,看到求连续段之和小于或大于某个值我们就可以想到双指针
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N=1e5+100;
int n,m;
int a[N],b[N];
int spicy[N];
int f[N][20];
void ST_pre_work()
{
for(int i=1;i<=n;i++) f[i][0]=b[i];
int t=log(n)/log(2)+1;
for(int j=1;j<t;j++)
for(int i=1;i<=n-(1<<j)+1;i++)
f[i][j]=max(f[i][j-1],f[i+(1<<(j-1))][j-1]);
}
int ST_ask(int l,int r)
{
int k=log(r-l+1)/log(2);
return max(f[l][k],f[r-(1<<k)+1][k]);
}
signed main()
{
freopen("feast.in","r",stdin);
freopen("feast.out","w",stdout);
cin>>n>>m;
for(int i=1;i<=n;i++) cin>>a[i]>>b[i];
ST_pre_work();
int tot=0;
int l=1,r=0;
while(r<=n)
{
while(tot>=m) tot-=a[l],spicy[l]=ST_ask(l,r),l++;
tot+=a[++r];
}
while(tot>=m) tot-=a[l],spicy[l]=ST_ask(l,r),l++;
sort(spicy+1,spicy+n+1);
for(int i=1;i<=n;i++)
if(spicy[i])
return cout<<spicy[i]<<endl,0;
}
T2 孤独的sxz
之前是在2023年的最后一场洛谷月赛碰到的这道题,那时候我们还不在这个机房,也没学什么东西,碰到这道题也不会,自然有一些畏难思想的,现在看来应该还是可以写,起码可以写60pts。我们显然发现选择边边角角会是最优的方案,感性理解的话因为这样可以充分利用自己对面最边上的那个点,而选择其它位置则一定无法做到这一点。理性证明的话可以用干扰法或者其它什么方法。
然后我们可以发现,如果边角被占了的话,那我们一定会选择其周围一圈的点。那我们就可以遍历最边角的四个点,并向外扩展,答案取最优即可。
现在问题是如何快速求解出你遍历到的当前点的权值。我们发现这个东西其实可以用一个类似于前缀和的东西去做。我选择的是前缀和加二分。处理出x轴与y轴的前缀和,然后每次都二分去找当前点的位置。该步时间复杂度是
好像也可以DFS,但是我没写。
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N=4e5+100;
int n,m,k;
int x[N],y[N];
int sa[N],sb[N];
vector<pair<int,int>> v;
set<pair<int,int>> s;
int calc(int a,int b)
{
if(a>n || a<1 || b<1 || b>m) return 0;
if(s.count({a,b})) return 0;
int it1=lower_bound(x+1,x+k+1,a)-x;
int it2=lower_bound(y+1,y+k+1,b)-y;
int px=((it1-1)*a-sa[it1-1])+sa[k]-sa[it1-1]-(k-it1+1)*a;
int py=((it2-1)*b-sb[it2-1])+sb[k]-sb[it2-1]-(k-it2+1)*b;
return px+py;
}
signed main()
{
freopen("sxz.in","r",stdin);
freopen("sxz.out","w",stdout);
cin>>n>>m>>k;
for(int i=1;i<=k;i++)
cin>>x[i]>>y[i],v.push_back({x[i],y[i]}),s.insert({x[i],y[i]});
sort(x+1,x+k+1),sort(y+1,y+k+1);
for(int i=1;i<=k;i++)
sa[i]=sa[i-1]+x[i],sb[i]=sb[i-1]+y[i];
int maxn=max({calc(1,1),calc(1,m),calc(n,1),calc(n,m)});
for(int i=1;i<=k;i++)
{
int nx=v[i-1].first,ny=v[i-1].second;
maxn=max({calc(nx,1),calc(nx,m),calc(1,ny),calc(n,ny),
calc(nx-1,ny),calc(nx,ny+1),calc(nx+1,ny),calc(nx,ny-1),
maxn});
}
return cout<<maxn<<endl,0;
}
T3 BST
我觉得看到这道题我们就可以直观的发现这道题它第一个难点就是读题,其实它的题意非常简单,但是它给你的伪代码就让这道题的读题难度上升了不止一个档次,也就让我这样的人连题都不想看(还是不可以畏难啊)。
形式化题意:现在有一个 1∼n 的排列 a,将序列中的元素依次放进一个 BST 里,求 BST 中插入函数的执行次数。(注意:第一个数已经作为 BST 的根。)
读完题我们就可以找到50pts的写法:直接按照题意所给模拟(这么快来伪代码还是有点用处的%%%%%)
然后考虑优化这个过程。显然发现,我们加入的一个节点一定会因为比它大的最小的点或者比它小的最大的点所更新。
然后就可以考虑使用单调栈解决这个问题了。(ZLC苣佬连单调栈都没有用,太厉害了)
可以从1开始模拟到n,找到第i个数左边比它小的数的最大值,再从n开始模拟到1,找到第i个数右边比它大的最小值,最后计算其深度即可。
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N=3e5+100;
int n,k,ans;
int a[N],x[N],d[N];
int depth[N],l[N],r[N],idx=1;
signed main()
{
freopen("bst.in","r",stdin);
freopen("bst.out","w",stdout);
cin>>n;
for(int i=1;i<=n;i++)
cin>>x[i],a[x[i]]=i;
for(int i=1;i<=n;i++)
{
while(a[i]<d[idx] && idx>1) idx--;
l[i]=x[d[idx]],d[++idx]=a[i];
}
int cnt=1;
for(int i=n;i>=1;i--)
{
while(a[i]<d[cnt] && cnt>1) cnt--;
r[i]=x[d[cnt]],d[++cnt]=a[i];
}
cout<<0<<endl;
for(int i=2;i<=n;i++)
{
depth[x[i]]=max(depth[l[x[i]]],depth[r[x[i]]])+1;
ans+=depth[x[i]];
cout<<ans<<endl;
}
return 0;
}
顺便膜拜一下张立宸巨佬的不用单调栈而用set的超美丽实现:code
T4 Pont des souvenirs
不想推式子(先咕这),只是以前写过(再加上一点点群众的力量),但是还是考场上没有写出来。找了一下午的问题,最后发现是我预处理逆元那些破玩意的时候太暴力了,每次都会多出来好几个常数(毕竟处理每个数的时候都用了个快速幂,开到2e7的时候常数还是有7的,比较的),还是不建议这样写了把。
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N=2e7+100,mod=1e9+7;
int n,k;
int fact[N],infact[N];
int qmi(int a,int b)
{
int res=1;
while(b)
{
if(b&1) res=res*a%mod;
a=a*a%mod;
b>>=1;
}
return res;
}
int C(int a,int b)
{
if(a<b) return 0;
return fact[a]*infact[a-b]%mod*infact[b]%mod;
}
void init()
{
fact[0]=infact[0]=1;
for(int i=1;i<N;i++) fact[i]=fact[i-1]*i%mod;
infact[N-1]=qmi(fact[N-1],mod-2);
for(int i=N-2;i;i--) infact[i]=infact[i+1]*(i+1)%mod;
}
void work()
{
cin>>n>>k;
int K=(k+1)/2;
cout<<(2*C(n+K-1,n)+C(n-2+K,n-1)*(k-2*K)%mod+mod)%mod<<endl;
}
signed main()
{
freopen("pont.in","r",stdin);
freopen("pont.out","w",stdout);
ios::sync_with_stdio(false);
ios::sync_with_stdio(false);
init();
int T;
cin>>T;
while(T--) work();
return 0;
}
最后再总结一下
挺好一套题,就是卡常卡空间不是太善良。。。。。。。。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 单线程的Redis速度为什么快?
· SQL Server 2025 AI相关能力初探
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 展开说说关于C#中ORM框架的用法!