【比赛记录】2025CSP+NOIP 冲刺模拟赛合集Ⅵ
11.20 HZOJ NOIP2025模拟赛12
| A | B | C | D | Sum | Rank |
|---|---|---|---|---|---|
| 66 | 20 | 66 | 12 | 164 | 18/34 |
A. 虫群之心
注意到 \(p-m\) 很小,于是 \(m!=\frac{(p-1)!}{\prod_{i=m+1}^{p-1}i}\),分子由威尔逊定理或打表可得出等于 \(p-1\)。
为什么不打表为什么不打表为什么不打表为什么不打表为什么不打表为什么不打表为什么不打表为什么不打表
Code
#include<bits/stdc++.h>
#define int long long
#define il inline
using namespace std;
namespace asbt{
int T,m,p;
il int qpow(int x,int y,int p){
int res=1;
while(y){
if(y&1){
res=res*x%p;
}
x=x*x%p,y>>=1;
}
return res;
}
int main(){
freopen("factorial.in","r",stdin);
freopen("factorial.out","w",stdout);
ios::sync_with_stdio(0),cin.tie(0);
cin>>T;
while(T--){
cin>>m>>p;
if(m>=p){
cout<<0<<'\n';
}else{
int res=p-1;
while(m!=p-1){
res=res*qpow(++m,p-2,p)%p;
}
cout<<res<<'\n';
}
}
return 0;
}
}
signed main(){return asbt::main();}
B. 喝醉的兔子
对于每一个查询 \(x\),由于我们最后要走到 \(kn,k\in\mathbb{Z}\),考虑同余最短路,初值为 \(dis_{(x+i)\bmod n}=0,i\in[L,R]\),对于每个 \(u\) 有边 \(u\to(du+i)\bmod n,i\in[L,R]\),bfs 即可,时间复杂度 \(O(Tqn^2)\)。
但这样直接建边空间是 \(O(n^2)\) 的。考虑 bfs 过程中每个点只有在第一次被更新时是有效的,而每个点的后继结点都是一段区间,于是我们可以用并查集来将已经更新过的区间覆盖掉,更新时跳并查集,空间复杂度线性。同时可以发现,此时每个点只会被更新一次,时间复杂度也来到了 \(O(Tqn)\)。
考虑进一步优化,所有询问的终点都是 \(0\),因此可以建反图,从 \(0\) 开始跑一遍 bfs 即可。但此时我们发现难以使用上面那个并查集的优化,因为在反图上每个点的后继结点并不是一段区间。我们来再次审视此时的连边:
对于所有 \(x\),如果 \(u\texttt{ s.t.}\exist i\in[L,R],(du+i)\bmod n=x\) 则有边 \(x\to u\)。将这个式子变形:
我们发现此时虽然 \(u\) 不是一个区间,但 \(x-i\) 是一个区间。于是我们对于每个 \(x-i\),扩欧求出有哪些 \(u\) 满足这个式子,挂一个 vector 即可。
查询时需要查的实际上是 \(\min_{i=L}^{R}\{dis_{(x+i)\bmod n}\}\),ST 表维护一下即可。总时间复杂度 \(O(Tn\log n)\),轻微卡常。
Code
#include<bits/stdc++.h>
#define il inline
#define pb push_back
using namespace std;
namespace asbt{
const int maxn=1e6+5,inf=1e9;
int T,n,d,l,r,m,fa[maxn],dep[maxn];
int q[maxn],hd,tl,st[21][maxn];
vector<int> vc[maxn];
il int exgcd(int a,int b,int &x,int &y){
if(!b){
x=1,y=0;
return a;
}
int d=exgcd(b,a%b,x,y),t=x;
x=y,y=t-a/b*y;
return d;
}
il int find(int x){
return x!=fa[x]?fa[x]=find(fa[x]):x;
}
il void push(int l,int r,int w){
for(int i=find(l);i<=r;i=find(i)){
for(int u:vc[i]){
dep[u]=w,q[++tl]=u;
}
fa[i]=find(i+1);
}
}
il void push(int x,int w){
int ll=(x-r+n)%n,rr=(x-l+n)%n;
if(ll<=rr){
push(ll,rr,w);
}else{
push(ll,n-1,w),push(0,rr,w);
}
}
il int query(int l,int r){
int p=__lg(r-l+1);
return min(st[p][l],st[p][r-(1<<p)+1]);
}
int main(){
freopen("calculate.in","r",stdin);
freopen("calculate.out","w",stdout);
ios::sync_with_stdio(0),cin.tie(0);
cin>>T;
while(T--){
cin>>n>>d>>l>>r>>m;
for(int i=0;i<=n;i++){
fa[i]=i,dep[i]=inf,vc[i].clear();
}
for(int i=1;i<=n;i++){
int x,y,g=exgcd(d,n,x,y);
if(i%g){
fa[i]=i+1;
}else{
// cout<<i<<": ";
y=n/g;
x=(x*1ll*(i/g)%y+y)%y;
if(x==0){
x=y;
}
while(x<n){
// cout<<x<<' ';
vc[i%n].pb(x);
x+=y;
}
// cout<<'\n';
}
}
hd=1,tl=0;
q[++tl]=0,dep[0]=0;
while(hd<=tl){
int u=q[hd++];
push(u,dep[u]+1);
}
for(int i=0;i<n;i++){
st[0][i]=dep[i];
}
for(int j=1;j<=19;j++){
for(int i=0;i+(1<<j)-1<n;i++){
st[j][i]=min(st[j-1][i],st[j-1][i+(1<<(j-1))]);
}
}
while(m--){
int x;
cin>>x;
int ll=(x+l)%n,rr=(x+r)%n,res;
if(ll<=rr){
res=query(ll,rr);
}else{
res=min(query(ll,n-1),query(0,rr));
}
cout<<(res>n+5?-1:res)<<'\n';
}
}
return 0;
}
}
int main(){return asbt::main();}
\(x\to u\) 的连边实际上可以枚举 \(u\) 找 \(x-i\) 做到线性,查询长为 \(R-L+1\) 的区间的最小值实际上可以用单调队列预处理,然后就能 \(O(Tn\alpha(n))\) 了。懒得写了
C. 盲盒流水线
直接猫树分治,类似「好吃的题目」,但由于要计数,只能在背包 DP 数组里记录体积恰好为 \(i\) 的最大价值和方案数。但这样查询时就不能直接 \(O(m)\) 合并了,时间复杂度 \(O(qm^2+nm\log n)\) 不是很能接受。解决方法也很好想,将两个需要合并的 DP 数组一个正着扫,另一个倒着扫,动态维护正着扫的那个当前的最大值及方案数即可。
为什么不取模为什么不取模为什么不取模为什么不取模为什么不取模为什么不取模为什么不取模为什么不取模
Code
#include<bits/stdc++.h>
#define ll long long
#define il inline
using namespace std;
namespace asbt{
const int maxn=2e4+5,maxm=1e5+5,inf=1e9,mod=998244353;
il int pls(int x,int y){
return x+y<mod?x+y:x+y-mod;
}
il void add(int &x,int y){
x=pls(x,y);
}
il int mns(int x,int y){
return x<y?x-y+mod:x-y;
}
il void sub(int &x,int y){
x=mns(x,y);
}
int n,m,a[maxn],b[maxn],ans1[maxm],ans2[maxm];
int f1[maxn][505],g1[maxn][505];
int f2[maxn][505],g2[maxn][505];
struct{
int l,r,w,id;
}c[maxm],d[maxm];
il void solve(int L,int R,int l,int r){
if(l>r){
return ;
}
// cerr<<L<<' '<<R<<' '<<l<<' '<<r<<'\n';
int mid=(L+R)>>1;
f1[mid+1][0]=0,g1[mid+1][0]=1;
for(int i=1;i<=500;i++){
f1[mid+1][i]=-inf,g1[mid+1][i]=0;
}
for(int i=mid;i>=L;i--){
for(int j=0;j<=500;j++){
f1[i][j]=f1[i+1][j],g1[i][j]=g1[i+1][j];
}
for(int j=500;j>=b[i];j--){
if(f1[i][j-b[i]]+a[i]>f1[i][j]){
f1[i][j]=f1[i][j-b[i]]+a[i];
g1[i][j]=g1[i][j-b[i]];
}else if(f1[i][j-b[i]]+a[i]==f1[i][j]){
add(g1[i][j],g1[i][j-b[i]]);
}
}
}
f2[mid][0]=0,g2[mid][0]=1;
for(int i=1;i<=500;i++){
f2[mid][i]=-inf,g2[mid][i]=0;
}
for(int i=mid+1;i<=R;i++){
for(int j=0;j<=500;j++){
f2[i][j]=f2[i-1][j],g2[i][j]=g2[i-1][j];
}
for(int j=500;j>=b[i];j--){
if(f2[i][j-b[i]]+a[i]>f2[i][j]){
f2[i][j]=f2[i][j-b[i]]+a[i];
g2[i][j]=g2[i][j-b[i]];
}else if(f2[i][j-b[i]]+a[i]==f2[i][j]){
add(g2[i][j],g2[i][j-b[i]]);
}
}
}
int p1=l,p2=r;
for(int i=l;i<=r;i++){
if(c[i].r<=mid){
d[p1++]=c[i];
continue;
}
if(c[i].l>mid){
d[p2--]=c[i];
continue;
}
int res1=-inf,res2=0;
for(int j=c[i].w,k=0;~j;j--,k++){
if(f2[c[i].r][k]>res1){
res1=f2[c[i].r][k],res2=g2[c[i].r][k];
}else if(f2[c[i].r][k]==res1){
add(res2,g2[c[i].r][k]);
}
if(f1[c[i].l][j]+res1>ans1[c[i].id]){
ans1[c[i].id]=f1[c[i].l][j]+res1;
ans2[c[i].id]=g1[c[i].l][j]*1ll*res2%mod;
}else if(f1[c[i].l][j]+res1==ans1[c[i].id]){
ans2[c[i].id]=(ans2[c[i].id]+g1[c[i].l][j]*1ll*res2)%mod;
}
}
}
for(int i=l;i<p1;i++){
c[i]=d[i];
}
for(int i=r;i>p2;i--){
c[i]=d[i];
}
solve(L,mid,l,p1-1);
solve(mid+1,R,p2+1,r);
}
int main(){
// system("fc sample_knapsack4.out my.out");
freopen("knapsack.in","r",stdin);
freopen("knapsack.out","w",stdout);
ios::sync_with_stdio(0),cin.tie(0);
cin>>n;
for(int i=1;i<=n;i++){
cin>>a[i]>>b[i];
}
cin>>m;
int cnt=0;
for(int i=1,l,r,w;i<=m;i++){
cin>>l>>r>>w;
if(l==r){
if(b[l]<=w){
ans1[i]=a[l],ans2[i]=1;
}else{
ans1[i]=ans2[i]=0;
}
}else{
c[++cnt]={l,r,w,i};
}
}
solve(1,n,1,cnt);
for(int i=1;i<=m;i++){
if(!ans1[i]){
ans2[i]=0;
}
cout<<ans1[i]<<' '<<ans2[i]<<'\n';
}
return 0;
}
}
signed main(){return asbt::main();}
D. 失落的帝国
首先考虑 \(m=n-1\),即不用考虑 mst 的限制,显然贪心,枚举 \(i\in[1,m]\),将所有 \(l_j\le i\) 中 \(r_j\) 最小的 \(j\) 的边权赋成 \(i\) 即可。
考虑一般情况,我们希望用 mst 这个条件对 \(l_j,r_j\) 进行一些限制,然后再用上面的贪心。考虑一条非树边 \(i\),树上 \(u_i\) 到 \(v_i\) 的路径上的最大边权应该 \(>w_i\)。记这条路径上的边为 \(k\),我们直接令 \(l_i\gets\max(l_i,l_k+1),r_k\gets\min(r_k,r_i-1)\)。显然这样不会将有节情况判成无解,且再用上面那个贪心就能满足 mst 的限制。
考虑树剖,我们要满足的操作就是求区间 \(\max\) 和区间取 \(\min\),用 ST 表和反向 ST 表即可。时间复杂度线性对数。
Code
#include<bits/stdc++.h>
#define ll long long
#define il inline
#define pb push_back
#define pii pair<int,int>
#define fir first
#define sec second
#define mp make_pair
using namespace std;
namespace asbt{
const int maxn=5e5+5;
int T,n,m,fa[maxn],dep[maxn];
int sz[maxn],hes[maxn],top[maxn];
int dfn[maxn],cnt,stk[maxn],c[maxn];
int st1[maxn][20],st2[maxn][20];
vector<int> e[maxn],b[maxn];
struct{
int u,v,l,r;
}a[maxn];
il void dfs1(int u){
sz[u]=1;
int mxs=0;
for(int v:e[u]){
if(v==fa[u]){
continue;
}
fa[v]=u;
dep[v]=dep[u]+1;
dfs1(v);
sz[u]+=sz[v];
if(mxs<sz[v]){
mxs=sz[v],hes[u]=v;
}
}
}
il void dfs2(int u){
if(!top[u]){
top[u]=u;
}
dfn[u]=++cnt,stk[cnt]=u;
if(hes[u]){
top[hes[u]]=top[u];
dfs2(hes[u]);
}
for(int v:e[u]){
if(v==fa[u]||v==hes[u]){
continue;
}
dfs2(v);
}
}
il int qry1(int l,int r){
if(l>r){
return 0;
}
int p=__lg(r-l+1);
return max(st1[l][p],st1[r-(1<<p)+1][p]);
}
il void upd2(int l,int r,int x){
if(l>r){
return ;
}
int p=__lg(r-l+1);
st2[l][p]=min(st2[l][p],x);
st2[r-(1<<p)+1][p]=min(st2[r-(1<<p)+1][p],x);
}
il int query(int u,int v){
int res=0;
while(top[u]!=top[v]){
if(dep[top[u]]<dep[top[v]]){
swap(u,v);
}
res=max(res,qry1(dfn[top[u]],dfn[u]));
u=fa[top[u]];
}
if(dep[u]>dep[v]){
swap(u,v);
}
return max(res,qry1(dfn[u]+1,dfn[v]));
}
il void upd(int u,int v,int x){
while(top[u]!=top[v]){
if(dep[top[u]]<dep[top[v]]){
swap(u,v);
}
upd2(dfn[top[u]],dfn[u],x);
u=fa[top[u]];
}
if(dep[u]>dep[v]){
swap(u,v);
}
upd2(dfn[u]+1,dfn[v],x);
}
int main(){
freopen("shi.in","r",stdin);
freopen("shi.out","w",stdout);
ios::sync_with_stdio(0),cin.tie(0);
cin>>T;
while(T--){
cin>>n>>m;
for(int i=1;i<=m;i++){
cin>>a[i].u>>a[i].v>>a[i].l>>a[i].r;
if(i<n){
e[a[i].u].pb(a[i].v);
e[a[i].v].pb(a[i].u);
}
}
cnt=0,dfs1(1),dfs2(1);
// for(int i=1;i<=n;i++){
// cout<<i<<' '<<fa[i]<<' '<<dep[i]<<' '<<sz[i]<<' '<<hes[i]<<' '<<top[i]<<' '<<dfn[i]<<'\n';
// }
for(int i=1;i<n;i++){
int u=a[i].u,v=a[i].v;
st1[dfn[dep[u]>dep[v]?u:v]][0]=a[i].l;
st2[dfn[dep[u]>dep[v]?u:v]][0]=a[i].r;
}
for(int j=1;j<=18;j++){
for(int i=2;i+(1<<j)-1<=n;i++){
st1[i][j]=max(st1[i][j-1],st1[i+(1<<(j-1))][j-1]);
st2[i][j]=max(st2[i][j-1],st2[i+(1<<(j-1))][j-1]);
}
}
for(int i=n;i<=m;i++){
a[i].l=max(a[i].l,query(a[i].u,a[i].v)+1);
upd(a[i].u,a[i].v,a[i].r-1);
}
for(int j=18;j;j--){
for(int i=2;i+(1<<j)-1<=n;i++){
st2[i][j-1]=min(st2[i][j-1],st2[i][j]);
st2[i+(1<<(j-1))][j-1]=min(st2[i+(1<<(j-1))][j-1],st2[i][j]);
}
}
for(int i=1;i<n;i++){
int u=a[i].u,v=a[i].v;
a[i].r=min(a[i].r,st2[dep[u]>dep[v]?dfn[u]:dfn[v]][0]);
}
priority_queue<pii> q;
for(int i=1;i<=m;i++){
b[a[i].l].pb(i);
// cout<<a[i].u<<' '<<a[i].v<<' '<<a[i].l<<' '<<a[i].r<<'\n';
}
for(int i=1;i<=m;i++){
for(int j:b[i]){
q.push(mp(-a[j].r,j));
}
if(q.empty()||-q.top().fir<i){
cout<<"NO\n";
goto togo;
}
c[q.top().sec]=i,q.pop();
}
cout<<"YES\n";
for(int i=1;i<=m;i++){
cout<<c[i]<<' ';
}
cout<<'\n';
togo:;
for(int i=1;i<=n;i++){
fa[i]=dep[i]=sz[i]=hes[i]=top[i]=dfn[i]=stk[i]=0;
e[i].clear();
}
for(int i=1;i<=m+1;i++){
b[i].clear();
}
}
return 0;
}
}
int main(){return asbt::main();}

浙公网安备 33010602011771号