cbc010&crc001 总结

1|0cbc010

本次比赛共 45 人报名,18 人提交,17 人有分,最高分为 @luogu___official 的 1996 分(六题)。恭喜 @Loser_syx 第一个 AC 六题,获得 5 元奖励。没有人获得 10 元奖励,也没有人获得 G 题结尾的神秘奖励。

至于为什么最高分不是 2050 分的 @Priestess_SLG,我们发现她的所有代码都和 @luogu___official 是一样的,所以鉴定为多号提交,该号的成绩不计入排名。

cbc 和 crc 的所有题解点赞均已送出。

1|1A

简单的不能再简单了。

1|2B

取反所有 0 连续段一定不劣。

1|3C

n 位若产生 1 的贡献,那么 2n1 位随便放即可,第一位必须放 1,共 2n2 的贡献。

n1 位若产生 1 的贡献,那么 2n2 位随便放即可,第一位必须放 1,第 n 位必须为 0,共 2n3 的贡献。

同理可得,答案即为 20+21++2n2=2n11

1|4D

dpi,j 表示以 i 为结尾,最后一段连续段的积为 j 的答案,则有:

dpi,j=max(dpk,l+j,k<i,x=ki1ax=j)

注意到第二维只有 3 种可能,分别记录三个出现过的最大值即可做到 O(n) 转移。

#include<bits/stdc++.h> #define N 100005 using namespace std; int n,a[N],dp[N]; map<int,int>qwq; int main(){ cin>>n; for(int i=1;i<=n;++i)cin>>a[i],dp[i]=-1e9;qwq[1]=qwq[-1]=-1e9; for(int i=1;i<=n;++i){ if(a[i]==0)qwq[0]=max({qwq[0],qwq[1],qwq[-1]}),qwq[1]=qwq[-1]=-1e9; else if(a[i]==-1)swap(qwq[-1],qwq[1]); qwq[a[i]]=max(qwq[a[i]],dp[i-1]); dp[i]=max({qwq[0],qwq[-1]-1,qwq[1]+1}); } cout<<dp[n]; return 0; }

我们甚至以为我们的数据错了,因为前两天的时候这题的通过情况和难度严重不符。

1|5E

建议改为:【模板】基环树最短路。

考虑找环之后断掉一条边,那么答案即为 树上最短路 和 强制走这条边的最短路 的最小值。所以直接 LCA 求树上路径长度即可。

1|6F

byd 没有一个人写正解,全是乱搞过的。

如果值域很大的话这是一个典型的不可做问题,所以必须从值域入手。

首先枚举 n 个数,这样问题转化为了在序列里找另一个数使得或起来最大。

考虑贪心地把答案从高位往低位填,那么我们需要快速判断这个位置能否填上 1,这看上去比较困难。我们可以分讨一下:

  • 该位为 1,显然选什么或起来都是 1,直接填 1

  • 该位为 0,设当前答案为 ans,那么需要判断是否存在一个数,使得与这个数或起来之后,得到的数的前面所有位与 ans 相同,该位为 1,后面随便。因为 ans 已经保证了前面尽可能大了,所以可以转化为:

求序列中是否存在一个数,使得每一个二进制位都大于等于给定数的对应位。

如果转化为集合来看,这相当于该数为给定数的超集,结合不大的值域,可以采用 sosdp 解决。

总的时间复杂度为 O((n+V)logV)

#include<bits/stdc++.h> #define N 2000005 using namespace std; int n,a[N],f[N],m=20,ans; int main(){ ios::sync_with_stdio(0);cin.tie(0);cout.tie(0); cin>>n; for(int i=1;i<=n;++i)cin>>a[i],f[a[i]]++; for(int i=1;i<=m;++i){ for(int j=0;j<(1<<m);++j){ if((j&(1<<i-1))==0)f[j]+=f[j^(1<<i-1)]; } } for(int i=1;i<=n;++i){ int sum=0,lxy=0; for(int j=m;j>=1;--j){ if(!(a[i]&(1<<j-1))){ int cnt=f[lxy|(1<<j-1)]; if((a[i]&(lxy|(1<<j-1)))==(lxy|(1<<j-1)))cnt--; if(cnt>0)sum+=1<<j-1,lxy+=1<<j-1; } else sum+=1<<j-1; } ans=max(ans,sum); } cout<<ans; return 0; }

接下来请我们欣赏人类群星闪耀时:

cin>>n; for(int i=1;i<=n;++i){ cin>>a[i]; box[a[i]]=1; for(int j=20;~j;--j) if(a[i]>>j&1)b[j]=max(b[j],a[i]); } for(int i=1;i<=n;++i) for(int j=20;~j;--j)mx=max(mx,a[i]|b[j]); int idx=0; for (int i=1000000;i;--i) { if(box[i]&&mx<i)mx=i; for(int j=0;j<box[i];++j)a[++idx]=i; } for(int i=1;i<=400;++i) for(int j=1;j<=n;++j)mx=max(mx,a[i]|a[j]); for(int i=0;i<64;++i){ int x=(seed*=1145141u)%n+1; for(int j=1;j<=n;++j)mx=max(mx,a[j]|a[x]); } cout<<mx<<'\n';

(节选自 @luogu___official 的代码)

int n = read(); for (int i=1;i<=n;++i) read(a[i]); int ans=0; sort(a+1,a+1+n); n=unique(a+1,a+1+n)-a-1; for (int i=max(1LL,n-1000);i<=n;++i) { for (int j=1;j<=i;++j) smax(ans,a[i]|a[j]); } write(ans);

(节选自 @Loser_syx 的代码)

1|7G

注:本题题解由 @Double_Light 提供,我们不保证这种做法分讨完全,但已经足以通过题目的数据。

考虑分类讨论。

以下称 A 表示 A 初始所在的位置,B 为 B 初始所在的位置,环内没有多余的边。

假如存在边 (A,B),那么 A 可以顺着这条边仅用 1 步赢得游戏。

否则假如 B 在一个 n 元环(n5)中,此时 A 有两种策略:

  • 在环上追逐 B,此时 B 只需要顺着 A 的方向走一步,A 永远追不上 B。

  • 走环外的道路,容易证明 B 如果也走同样的环外道路,A 永远追不上 B。

假如 B 在一个四元环中,考虑继续分情况讨论:

  • A 无法花奇数步到达 B,此时 B 只需要在环上绕圈就可以赢。
  • A 可以花奇数步到达 B(这意味着图上一定有一个奇环),此时 B 不能一直在环上绕圈。

假如 B 在一个三元环中,此时 B 一定不能在环上绕圈,因为一旦 A 走到环上 B 就需要从环上出去。

那么现在有两种情况没有解决,也就是 B 在四元环中且 A 可以花奇数步到达 B 以及 B 在三元环中。对于这两种情况,B 都需要寻找一个环,到达环上且一直在环上绕圈。根据刚才的分析,这个环应该满足如下条件之一:

  • 环上点的个数 5。(条件 1
  • 是一个四元环且 A 无法花奇数步到达 B 可以在奇数步中到达的点。(条件 2

经过简单的分析可以得到条件 2 是不可能达到的。假如 A 无法花奇数步到达 B 可以在奇数步中到达的点,就意味着图上不存在点数是奇数的环,也就是图中只存在四元环或这满足 n5n 元环。在这两种情况下,B 只需要在环上绕圈就可以赢。

所以 B 需要找一个满足条件 1 的环(以下称之为大环)。分如下几种情况:

  • 图上不存在满足条件的环,此时 B 必输。
  • 图上存在一个满足条件的环。
    • A 不在大环上。
      • A 一步可以到达中心点。A 赢。
      • A 一步不能到达中心点且 B 一步可以到达环上。B 赢。
      • A 一步不能到达中心点且 B 一步不能到达环上。A 赢。
    • B 所在的所有环都与大环没有公共边。
      • A 离中心点的距离大于 B 离中心点距离 +2,A 必输。
      • A 离大环两边与中心点连边的点距离都为 2
        • 如果 B 顺时针与逆时针都能满足条件 (1) 或条件 (3) 则 B 赢。
        • 否则 B 会输。
      • 否则不妨设 A 顺时针(逆时针同理)走距离大环中与中心点连边的点更近。
        • A 离中心点距离为 1
          • 若 B 在顺时针或逆时针上满足条件 (1) 则 B 赢。
          • 否则 A 赢。
        • A 离中心点距离为 2
          • 若 B 在顺时针或逆时针上满足条件 (0) 或条件 (2) 则 B 赢。
          • 若 B 顺时针走一步可以到达大环则 B 赢。
          • 否则 A 赢。
        • A 离中心点距离为 3
          • 若 B 在顺时针或逆时针上满足条件 (1) 或条件 (3) 则 B 赢。
          • 若 B 顺时针走一步或两步可以到达大环则 B 赢。
          • 否则 A 赢。
    • B 所在的一个环与大环有公共边。
      • 假设这条边为 (x,y),若 A 不在 xy 上,且顺时针(逆时针同理)走一步能到达 xy
        • 若 B 在顺时针方向上满足条件 (0)(2),B 赢。
        • 若 B 顺时针走一步可以到达大环,B 赢。
        • 否则 A 赢。
      • 否则 B 能赢。
  • 图上存在不止一个满足条件的环。把上面『满足条件的环』改成『所有满足条件的环』就行了。只要有一个环满足条件 B 就能赢。

:若 B 沿某方向走 i 步可以到达不与中心点连边的点,则称其在该方向上满足条件 (i)

后来 @Double_Light 给了一种更简单的做法,就是直接写有向图博弈,如果 k 步以内追不上判无解,其中 k 是一个比较小的数。


我们发现题面最结尾的空白处有一段话,如果知道为什么比赛在 28 日结束就可以获得神秘奖励,但没有人猜中。

答案是,28 日是 cbc 的生日,也是鸽游的生日。祝它们生日快乐!

2|0crc001

15 人报名,5 人有分,最高分为 @Loser_syx 的 943 分。

2|1C

为了方便,下文把题目中的两个变量 d,q 改名成 c1,c2

我们可以证明,fn 一定可以表示为 k1rn+k2sn 的形式。其中 r=c1+c124c22s=c1c124c22,证明请参考 link

因为我们知道前两项,所以可以解方程组算出 k1,k2。注意因为前面的 r,s 带有根号,所以我们一整个过程都需要进行括域处理,即将每个数表示为 a+bBase,其中 Base=c124c2

然后就是推式子:

i=1n(k1ri+k2si)k

=i=1nj=0kCkj(k1ri)j×(k2si)kj

=i=1nj=0kCkjk1jk2kjrijsi(kj)

=j=0k(Ckjk1jk2kji=1nrijsi(kj))

t=rjskj,则原式等于:

j=0k(Ckjk1jk2kji=1nti)

=j=0kCkjk1jk2kjt(tn1)t1

当然这之前要特判掉 t=1

接下来我们发现,这个式子可以在 O(klogn) 的时间复杂度内求出,所以我们就做完了……吗?

注意到括域过程中涉及除法,这之中很有可能出现除以一个取模之后得到的的情况,因为它没有逆元,就会出现错误。

但是注意到除法的过程中分母是 a2b2Base 的形式,而这个为 0 相当于是 a2b2Base,这相当于 Base 是模 p 意义下的二次剩余!所以直接用 Cipolla 算法求出二次剩余的解就不用括域了。这样我们就完全解决了这道题目。

注意到其实还有一种 rs 的情况,但因为本题 d,q104 的限制,这是不可能出现的。并且如果存在这种情况,那么本题可能无法在给定的数据范围下求解。

2|2D

首先一眼先把 b 前缀和。

如果没有修改的话,相当于两个多项式相乘,我们直接用 NTT 就可以求出答案。

加上修改之后,我们发现几乎没有办法维护,于是直接暴力对操作分块,每 B 次操作重构一次,令 n,q 同阶,时间复杂度 O(n2lognB+nB)。取 B=nlogn,可得最优时间复杂度 O(nnlogn)

#include<bits/stdc++.h> #define int long long #define N 270005 using namespace std; const int B=7000,mod=998244353; int n,q,b[N],f[N],g[N],S[N],rev[N],k=1,qwq,ans[N]; int rr[N][2],t,sg[N]; int qpow(int x,int y){ int ans=1; while(y){ if(y&1)ans=ans*x%mod; x=x*x%mod;y>>=1; } return ans; } int qm(int x){ x>mod?x-=mod:0; return x; } void NTT(int n,int *a,int op){ for(int i=0;i<n;++i){ if(i<rev[i])swap(a[i],a[rev[i]]); } for(int len=1;len<=n/2;len*=2){ int u=qpow(op==1?3:332748118,(mod-1)/(len*2)); for(int i=0;i<=n-len*2;i+=len*2){ int w=1; for(int j=0;j<len;++j){ int x=a[i+j],y=w*a[i+j+len]%mod; a[i+j]=qm(x+y),a[i+j+len]=qm(x-y+mod); w=w*u%mod; } } } } signed main(){ ios::sync_with_stdio(0);cin.tie(0);cout.tie(0); cin>>n>>q; for(int i=1;i<=n;++i){ int x;cin>>x;S[i]=qm(S[i-1]+x); } for(int i=0;i<n;++i){ cin>>g[i];if(i>0)g[i]=qm(g[i]+g[i-1]); sg[i+1]=qm(sg[i]+g[i]); } while(k<=n*2)++qwq,k*=2; for(int i=1;i<k;++i)rev[i]=(rev[i>>1]>>1)|((i&1)<<(qwq-1)); NTT(k,g,1); for(int c=1;c<=q;++c){ int op,l,r;cin>>op>>l>>r; if(c%B==0){ NTT(k,f,1); for(int i=0;i<=k;++i)ans[i]=f[i]*g[i]%mod; NTT(k,ans,-1);int inv=qpow(k,mod-2); for(int i=0;i<n;++i)ans[i]=(ans[i]+mod)*inv%mod; for(int i=0;i<n;++i)ans[i+1]=qm(ans[i+1]+ans[i]); for(int i=0;i<n;++i)S[i+1]=qm(S[i+1]+ans[i]); memset(f,0,sizeof(f));t=0; } if(op==1)f[l-1]=qm(f[l-1]+r),rr[++t][0]=l,rr[t][1]=r; else{ int ans=0; for(int i=1;i<=t;++i){ int ql=max(l,rr[i][0]),qr=r; if(ql>qr)continue; if(qr-rr[i][0]+1>0)ans+=(sg[qr-rr[i][0]+1]-sg[ql-rr[i][0]])*rr[i][1]%mod; } ans+=S[r]-S[l-1]; cout<<(ans%mod+mod)%mod<<'\n'; } } return 0; }

__EOF__

本文作者Luminescent冷光
本文链接https://www.cnblogs.com/Milthm/p/18706533.html
关于博主:评论和私信会在第一时间回复。或者直接私信我。
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!
声援博主:如果您觉得文章对您有帮助,可以点击文章右下角推荐一下。您的鼓励是博主的最大动力!
posted @   Milthm  阅读(16)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 使用C#创建一个MCP客户端
· ollama系列1:轻松3步本地部署deepseek,普通电脑可用
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· 按钮权限的设计及实现
点击右上角即可分享
微信分享提示