2025多校冲刺省选模拟赛1
2025多校冲刺省选模拟赛1
A. 切割蛋糕(cake)
-
令
。 -
设
Alice
选择了 块蛋糕,总和为 ,则限制条件为 ,移项得到 。 -
设以
为起点,则需要保证 ,移项后得到 ,分别前后缀取 判断即可。- 需要边界特判
时可选的只有 , 时可选的只有 ,但数据貌似没卡。
点击查看代码
ll a[500010],sum[500010],flag[2][500010]; int main() { #define Isaac #ifdef Isaac freopen("cake.in","r",stdin); freopen("cake.out","w",stdout); #endif ll n,ans=-1,minn=0x7f7f7f7f7f7f7f7f,i; scanf("%lld",&n); for(i=1;i<=n;i++) { scanf("%lld",&a[i]); sum[i]=sum[i-1]+a[i]; } for(i=n;i>=1;i--) { minn=min(minn,sum[i]*n-sum[n]*i); flag[0][i]=(minn>=sum[i-1]*n-sum[n]*i+sum[n]); } minn=0x7f7f7f7f7f7f7f7f; for(i=n-1;i>=1;i--) { minn=min(minn,sum[i]*n-sum[n]*i); } flag[0][1]=flag[1][1]=(minn>=0); flag[1][2]=1; minn=0x7f7f7f7f7f7f7f7f; for(i=1;i<=n;i++) { minn=min(minn,sum[i]*n-sum[n]*i); flag[1][i+2]=(minn>=sum[i+2-1]*n-sum[n]*(i+2)+sum[n]); } for(i=1;i<=n;i++) { if(flag[0][i]==1&&flag[1][i]==1) { ans=i; break; } } printf("%lld\n",ans); return 0; }
- 需要边界特判
B. 游乐园(park)
-
反悔贪心,因后面替换前面的
时可以保留 ,否则直接删掉 ,故需要额外开两个小根堆存储当前未被使用 的贡献,需要懒惰删除。点击查看代码
ll vis[200010]; pair<ll,ll>a[200010]; priority_queue<pair<ll,ll>,vector<pair<ll,ll> >,greater<pair<ll,ll> > >q1,q2,q3; int main() { #define Isaac #ifdef Isaac freopen("park.in","r",stdin); freopen("park.out","w",stdout); #endif ll n,k,t,sum=0,ans=0,i; cin>>n>>k>>t; for(i=1;i<=n;i++) { cin>>a[i].second>>a[i].first; } sort(a+1,a+1+n); for(i=1;i<=n;i++) { if(q1.size()>=k||sum+a[i].first>t) { q2.push(make_pair(a[i].first,i)); q3.push(make_pair(a[i].second,i)); } else { if(sum+a[i].first<=t) { sum+=a[i].first; ans++; q1.push(make_pair(a[i].second-a[i].first,i)); } } } for(i=q1.size()+1;i<=n;i++) { while(q2.empty()==0&&vis[q2.top().second]==1) { q2.pop(); } while(q3.empty()==0&&vis[q3.top().second]==1) { q3.pop(); } if(q1.empty()==0) { if(sum+min(q3.top().first,q2.top().first+q1.top().first)<=t) { sum+=min(q3.top().first,q2.top().first+q1.top().first); ans++; } if(q3.top().first<q2.top().first+q1.top().first) { vis[q3.top().second]=1; q3.pop(); } else { vis[q2.top().second]=1; q1.pop(); q1.push(make_pair(a[q2.top().second].second-q2.top().first,q2.top().second)); q2.pop(); } } else { if(sum+q3.top().first<=t) { sum+=q3.top().first; ans++; } vis[q3.top().second]=1; q3.pop(); } } cout<<ans<<endl; return 0; }
-
放两组
数据。点击查看数据 1
in: 10 1 8467 7058 57 4082 2989 1015 273 9569 2307 219 217 1953 433 8659 5494 4289 2050 3391 1301 9734 3199 ans: 5
点击查看数据 2
in: 5 3 13 17 16 18 15 19 14 17 14 17 15 ans: 0
C. 有根树(tree)
-
部分分
-
:枚举全排列。点击查看代码
const ll mod=998244353; struct node { ll nxt,to; }e[500010]; ll head[500010],fa[500010],col[2][500010],p[500010],cnt=0; void add(ll u,ll v) { cnt++; e[cnt].nxt=head[u]; e[cnt].to=v; head[u]=cnt; } void access(ll x,ll id) { for(ll i=head[x];i!=0;i=e[i].nxt) { col[id][i]=0; } for(;x!=1;x=fa[x]) { for(ll i=head[fa[x]];i!=0;i=e[i].nxt) { col[id][i]=(e[i].to==x); } } } ll ask(ll n) { ll ans=0,flag; for(ll i=1;i<=n;i++) { p[i]=i; } do { flag=1; fill(col[1]+1,col[1]+1+cnt,0); for(ll i=1;i<=n;i++) { access(p[i],1); } for(ll i=1;i<=cnt&&flag==1;i++) { flag&=(col[0][i]==col[1][i]); } ans=(ans+flag)%mod; }while(next_permutation(p+1,p+1+n)); return ans; } int main() { #define Isaac #ifdef Isaac freopen("tree.in","r",stdin); freopen("tree.out","w",stdout); #endif ll n,m,x,i; scanf("%lld%lld",&n,&m); for(i=2;i<=n;i++) { scanf("%lld",&fa[i]); add(fa[i],i); } for(i=1;i<=m;i++) { scanf("%lld",&x); access(x,0); printf("%lld\n",ask(n)); } return 0; }
-
-
正解
- 由实链剖分和
access
操作定义可知操作过程中每个点连向儿子的边中最多有一条实边。 - 定义一个节点也是一条实链。
- 观察到一条极长实链
中链底节点 一定是链上节点中最后一次被操作的,其他节点的顺序随意。同时 比 所在实链链底节点 操作靠前。对这棵树进行重构,非链底节点向所在链底连边,链底节点向父亲所在链底连边,限制条件转化为儿子节点比父亲节点先操作。 - 此时转化为了 树的拓扑序计数 ,易得递推式
,全局答案为 。 - 在上面的式子中,重构出的树只有原树上的链底节点的
不为 (不考虑只有一个点的实链)且等于链顶节点在原树上的 。等价于查询 ,其中 为链顶集合。难点在于如何维护链顶集合 。 - 此时
辅助换根 已经很容易维护了,详见 [ABC160F] Distributing Integers 。考虑树剖怎么维护。 - 每次
access
操作对一条重链的影响是若干段的链顶被清空,每段的结尾被加入 。故可以对每条重链开一个栈手动模拟set
自浅到深维护极长实链段,由颜色端均摊理论可知时间复杂度为 。 - 具体实现时,每个极长实链段分别维护其在这条重链的开头、实链在这条重链上的转折点(若没有另一条与其相连重链则记为原值,否则记为另一条与其相连的重链链顶)、实链的结尾,每个节点分别维护其所在实链链顶。
- 清空时分讨完全包含或部分包含,前者要特判结尾分裂产生的影响,后者直接分裂成两部分。
点击查看代码
const ll p=998244353; struct node { ll nxt,to; }e[500010]; ll head[500010],fa[500010],inv[500010],siz[500010],son[500010],dep[500010],top[500010],col_top[500010],cnt=0,ans=1; struct quality { mutable ll l; ll r,ed; }it; stack<quality>s[500010]; void add(ll u,ll v) { cnt++; e[cnt].nxt=head[u]; e[cnt].to=v; head[u]=cnt; } void dfs1(ll x) { siz[x]=1; dep[x]=dep[fa[x]]+1; for(ll i=head[x];i!=0;i=e[i].nxt) { dfs1(e[i].to); siz[x]+=siz[e[i].to]; son[x]=(siz[e[i].to]>siz[son[x]])?e[i].to:son[x]; } ans=ans*x%p*inv[siz[x]]%p; } void dfs2(ll x,ll id) { top[x]=id; col_top[x]=x; if(son[x]!=0) { dfs2(son[x],id); for(ll i=head[x];i!=0;i=e[i].nxt) { if(e[i].to!=son[x]) { dfs2(e[i].to,e[i].to); } } } s[top[x]].push((quality){x,x,x}); } void update(ll x) { ans=ans*inv[siz[1]]%p; ll id=x,last=x; while(x!=0) { while(s[top[x]].empty()==0&&dep[s[top[x]].top().l]<=dep[x]) { it=s[top[x]].top(); if(dep[it.r]-(top[it.r]!=top[x])>dep[x])//不完全包含,需要分裂 //-(top[it.r]!=top[x]) 是为了找到在本条重链上的转折点 { ans=ans*siz[col_top[it.ed]]%p*inv[siz[son[x]]]%p; s[top[x]].top().l=col_top[it.ed]=son[x]; } else { if(dep[col_top[it.ed]]<=dep[it.l])//完全包含时在首次遍历到这条实链后就分裂开更新答案 { ans=ans*siz[col_top[it.ed]]%p*((top[it.r]!=top[x])?inv[siz[it.r]]:1)%p; //top[it.r]!=top[x] 对应有其他重链连接,否则由极长实链可知其不会产生影响 col_top[it.ed]=it.r; } s[top[x]].pop(); } } s[top[x]].push((quality){top[x],last,id});//last记录另一条与其相交的重链链顶 last=top[x]; x=fa[top[x]]; } col_top[id]=1; } int main() { #define Isaac #ifdef Isaac freopen("tree.in","r",stdin); freopen("tree.out","w",stdout); #endif ll n,m,x,i; scanf("%lld%lld",&n,&m); inv[1]=1; for(i=2;i<=n;i++) { scanf("%lld",&fa[i]); add(fa[i],i); inv[i]=(p-p/i)*inv[p%i]%p; } dfs1(1); dfs2(1,1); for(i=1;i<=m;i++) { scanf("%lld",&x); update(x); printf("%lld\n",ans); } return 0; }
- 由实链剖分和
D. 集合操作(set)
-
部分分
-
:爆搜。点击查看代码
ll p,ans=0; deque<ll>s[1010],q; ll qpow(ll a,ll b,ll p) { ll ans=1; while(b) { if(b&1) { ans=ans*a%p; } b>>=1; a=a*a%p; } return ans; } void dfs(ll dep,ll w,ll mul) { if(dep!=0) { s[dep]=s[dep-1]; for(ll i=0;i<s[dep].size();i++) { if(s[dep][i]%w==0) { q.push_back(i); } } while(q.empty()==0) { s[dep].erase(q.back()+s[dep].begin()); q.pop_back(); } } if(s[dep].size()==0) { ans=(ans+dep*mul%p)%p; return; } mul=mul*qpow(s[dep].size(),p-2,p)%p; for(ll i=0;i<s[dep].size();i++) { dfs(dep+1,s[dep][i],mul); } } int main() { #define Isaac #ifdef Isaac freopen("set.in","r",stdin); freopen("set.out","w",stdout); #endif ll n,i; cin>>n>>p; for(i=1;i<=n;i++) { s[0].push_back(i); } dfs(0,0,1); cout<<ans<<endl; return 0; }
-
-
类似 CF280C Game on Tree ,依据期望线性性,考虑每个数被用于增加次数的期望,得到
即为所求。 -
线性筛或暴力求即可。
点击查看代码
ll p,prime[100010],f[100010],low[100010],nu[100010],vis[100010],len=0; ll qpow(ll a,ll b,ll p) { ll ans=1; while(b) { if(b&1) { ans=ans*a%p; } b>>=1; a=a*a%p; } return ans; } void isprime(ll n) { memset(vis,0,sizeof(vis)); f[1]=1; for(ll i=2;i<=n;i++) { if(vis[i]==0) { len++; prime[len]=i; nu[i]=1; low[i]=i; f[i]=qpow(2,p-2,p); } for(ll j=1;j<=len&&i*prime[j]<=n;j++) { vis[i*prime[j]]=1; if(i%prime[j]==0) { low[i*prime[j]]=low[i]*prime[j]; nu[i*prime[j]]=nu[i]+1; if(i==low[i]) { f[i*prime[j]]=qpow(nu[i*prime[j]]+1,p-2,p); } else { f[i*prime[j]]=f[i/low[i]]*f[low[i*prime[j]]]%p; } } else { low[i*prime[j]]=prime[j]; nu[i*prime[j]]=prime[j]; f[i*prime[j]]=f[i]*f[prime[j]]%p; } } } } int main() { #define Isaac #ifdef Isaac freopen("set.in","r",stdin); freopen("set.out","w",stdout); #endif ll n,ans=0,i; cin>>n>>p; isprime(n); for(i=1;i<=n;i++) { ans=(ans+f[i])%p; } cout<<ans<<endl; return 0; }
-
-
-
正解
总结
没想到还可以直接删掉 ,挂了 。 赛时发现的链长为 的性质无法直接扩展到长度为 的性质。
本文来自博客园,作者:hzoi_Shadow,原文链接:https://www.cnblogs.com/The-Shadow-Dragon/p/18648514,未经允许严禁转载。
版权声明:本作品采用 「署名-非商业性使用-相同方式共享 4.0 国际」许可协议(CC BY-NC-SA 4.0) 进行许可。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 无需6万激活码!GitHub神秘组织3小时极速复刻Manus,手把手教你使用OpenManus搭建本
· C#/.NET/.NET Core优秀项目和框架2025年2月简报
· Manus爆火,是硬核还是营销?
· 一文读懂知识蒸馏
· 终于写完轮子一部分:tcp代理 了,记录一下