【比赛记录】2025CSP-S模拟赛16
| A | B | C | D | Sum | Rank |
|---|---|---|---|---|---|
| 36 | 25 | 0 | 25 | 86 | 13/21 |
超绝降智场😍
A. 醉
首先求出一条直径,一个显然的结论是从任何一个点出发,直径的两个端点之一一定是能走到的最远的点。这可以用反证法证明。于是问题变成以直径端点为根的 \(k\) 级祖先。离线下来跑 dfs 即可。时间复杂度线性。
Code
#include<bits/stdc++.h>
#define ll long long
#define il inline
#define pb push_back
#define pii pair<int,int>
#define mp make_pair
#define fir first
#define sec second
using namespace std;
namespace asbt{
const int maxn=2e5+5;
int n,m,dep[maxn],mxd[maxn],mxp[maxn];
int dd[maxn],ans[maxn];
vector<int> e[maxn];
vector<pii> q[maxn];
il void dfs1(int u,int fa){
mxd[u]=dep[u]=dep[fa]+1;
mxp[u]=u;
for(int v:e[u]){
if(v==fa){
continue;
}
dfs1(v,u);
if(mxd[u]<mxd[v]){
mxd[u]=mxd[v];
mxp[u]=mxp[v];
}
}
}
il void dfs2(int u,int fa){
dep[u]=dep[fa]+1;
dd[dep[u]]=u;
for(pii i:q[u]){
int d=i.fir,id=i.sec;
if(ans[id]==-1&&dep[u]>d){
ans[id]=dd[dep[u]-d];
}
}
for(int v:e[u]){
if(v==fa){
continue;
}
dfs2(v,u);
}
}
int main(){
ios::sync_with_stdio(0),cin.tie(0);
cin>>n;
for(int i=1,u,v;i<n;i++){
cin>>u>>v;
e[u].pb(v),e[v].pb(u);
}
dfs1(1,0);
int d1=mxp[1];
dfs1(d1,0);
int d2=mxp[d1];
cin>>m;
for(int i=1,u,d;i<=m;i++){
cin>>u>>d;
q[u].pb(mp(d,i));
}
memset(ans,-1,sizeof(ans));
dfs2(d1,0),dfs2(d2,0);
for(int i=1;i<=m;i++){
cout<<ans[i]<<"\n";
}
return 0;
}
}
int main(){return asbt::main();}
B. 与
设 \(f_{i,j}\) 表示 \(i\) 左侧最大的第 \(j\) 为 \(1\) 的点,容易递推求出。
设 \(g_{i,j}\) 表示 \(i\) 左侧第 \(j\) 位为 \(1\) 最大的能走到 \(i\) 的点。枚举从第 \(k\) 位走到 \(i\),于是有转移:
-
\(f_{i,k}\) 第 \(j\) 位为 \(1\),\(g_{i,j}\leftarrow f_{i,k}\)。
-
\(g_{i,j}\leftarrow g_{f_{i,k},j}\)。因为 \(f_{i,k}\) 是最大的,从它转移一定最优。
Code
#include<bits/stdc++.h>
#define ll long long
#define il inline
using namespace std;
namespace asbt{
const int maxn=3e5+5;
int n,m,a[maxn],f[maxn][22],g[maxn][22];
int main(){
ios::sync_with_stdio(0),cin.tie(0);
cin>>n>>m;
for(int i=1;i<=n;i++){
cin>>a[i];
for(int j=0;j<=19;j++){
if(a[i-1]>>j&1){
f[i][j]=i-1;
}
else{
f[i][j]=f[i-1][j];
}
}
}
for(int i=1;i<=n;i++){
for(int j=0;j<=19;j++){
for(int k=0;k<=19;k++){
if(a[i]>>k&1){
if(a[f[i][k]]>>j&1){
g[i][j]=max(g[i][j],f[i][k]);
}
g[i][j]=max(g[i][j],g[f[i][k]][j]);
}
}
}
}
while(m--){
int l,r;
cin>>l>>r;
for(int i=0;i<=19;i++){
if((a[l]>>i&1)&&g[r][i]>=l){
cout<<"Shi\n";
goto togo;
}
}
cout<<"Fou\n";
togo:;
}
return 0;
}
}
int main(){return asbt::main();}
C. 小恐龙
将陨石块分块,预处理出每个块在 \(i\) 时间内能增长的魔法值 \(f_i\)。对于每个小恐龙,枚举每一个块,如果先看这个块有没有被清空过,被清空过就判断它能不能把小恐龙跑死,跑死就直接跑,跑不死就直接用 \(f\) 数组处理答案;如果没清空过那就只好暴力跑了,但显然每一个块顶多这样暴力一次,因此总复杂度 \(O((n+q)\sqrt{n})\)。空间限制比较吃紧,因此需要一个块一个块地跑,于是空间是线性的。
Code
#include<bits/stdc++.h>
#define int long long
#define il inline
using namespace std;
namespace asbt{
const int maxn=4e5+5,B=450,T=4e5;
int n,m,c[maxn],r[maxn],t[maxn],h[maxn];
int a[maxn],f[maxn],bn,L[505],R[505];
bool flag;
il void work(int x,int y,int t){
flag=0;
for(int i=L[x];i<=R[x];i++){
a[i]=min(a[i]+r[i]*t,c[i]);
if(a[i]<=h[y]){
h[y]-=a[i],a[i]=0;
}
else{
flag=1;
a[i]-=h[y],h[y]=0;
}
}
}
il void solve(int x){
memset(f,0,sizeof(f));
for(int i=L[x];i<=R[x];i++){
f[1]+=r[i],f[min(c[i]/r[i],T)+1]-=r[i];
}
for(int i=1;i<=T;i++){
f[i]+=f[i-1];
}
for(int i=L[x];i<=R[x];i++){
f[min(c[i]/r[i],T)+1]+=c[i]%r[i];
}
for(int i=1;i<=T;i++){
f[i]+=f[i-1];
}
flag=1;
for(int i=1,lst=0;i<=m;i++){
if(!h[i]){
continue;
}
int tmp=t[i]-lst;
if(flag){
work(x,i,tmp);
}
else if(h[i]<=f[tmp]){
work(x,i,tmp);
}
else{
h[i]-=f[tmp];
}
lst=t[i];
}
}
int main(){
ios::sync_with_stdio(0),cin.tie(0);
cin>>n;
for(int i=1;i<=n;i++){
cin>>c[i]>>r[i];
a[i]=c[i];
}
cin>>m;
for(int i=1;i<=m;i++){
cin>>t[i]>>h[i];
}
bn=(n+B-1)/B;
for(int i=1;i<=bn;i++){
L[i]=R[i-1]+1;
R[i]=min(R[i-1]+B,n);
}
for(int i=1;i<=bn;i++){
solve(i);
}
int ans=0;
for(int i=1;i<=m;i++){
ans+=h[i];
}
cout<<ans;
return 0;
}
}
signed main(){return asbt::main();}
/*bb*/

考场唯一成果→
浙公网安备 33010602011771号