牛客 周赛77 20250222
牛客 周赛77 20250222
A:
题目大意:给定 \(n\) 输出指定字符串
#include<bits/stdc++.h> using namespace std; int main() { int n; cin>>n; if (n==1) cout<<20250121; if (n==2) cout<<20250123; if (n==3) cout<<20250126; if (n==4) cout<<20250206; if (n==5) cout<<20250208; if (n==6) cout<<20250211; return 0; }
签到
B:
题目大意:给定一个数组,判断重新排序后能否组成一个数组数组,当且仅当每个长度为 \(9\) 的连续子数组,都包含 \([1,9]\) 这 \(9\) 个数字
#include<bits/stdc++.h> using namespace std; int a[10]; int main() { int n; cin>>n; int x; for (int i=0;i<n;i++){ cin>>x; a[x]++; } int b=n/9; bool f=1; for (int i=1;i<=9;i++){ if (a[i]<b||a[i]>b+1){ f=0; } } if (f) cout<<"YES"; else cout<<"NO"; return 0; }
当且仅当数组内每个元素个数为 \(b,b+1\) 时满足题意
因为数组数组重新排列后可以组成一个特殊序列,即
每 \(9\) 个元素为一组,那么组数为 \(\lfloor\frac{n}{9}\rfloor\) ,多余的元素同样的在后面依次排列
C:
题目大意:给定二维平面直角坐标系,初始位置为 \((0,0)\) ,每次可以向上,下,左,右分别移动 \(a,b,c,d\) 的距离,判断给定的 \((x,y)\) 能否通过任意步数走到
#include<bits/stdc++.h> using namespace std; void solve(void){ int x,y,a,b,c,d; cin>>x>>y>>a>>b>>c>>d; int u=__gcd(a,b),v=__gcd(c,d); if (x%v==0&&y%u==0) cout<<"YES"<<endl; else cout<<"NO"<<endl; } int main() { int T; cin>>T; while(T--) solve(); return 0; }
证明如下:
假设存在整数 \(x\) 能由整数 \(i,j\) 线性组合而成,那么可以写作:
设 \(i,j\) 的最小公约数为 \(p\) ,则有:
所以判断 \(x\) 能否由 \(i,j\) 线性组合而成只需要判断 \(x\) 能否由 \(gcd(i,j)\) 线性组合而成
D:
题目大意:
#include<bits/stdc++.h> #define cintie ios::sync_with_stdio(false);cin.tie(0);cout.tie(0); #define Trd int T;cin>>T;while (T--)solve(); #define LLinf 9e18; #define Iinf 2e9 #define LL long long #define Lc p<<1 #define Rc p<<1|1 #define lc(x) tr[x].ch[0] #define rc(x) tr[x].ch[1] using namespace std; int n; int cnt[100100]; int fa[100100]; int find(int x){ if (fa[x]==x) return x; else return fa[x]=find(fa[x]); } void merge(int x,int y){ int fx=find(x),fy=find(y); if (fx!=fy){ fa[fx]=fy; cnt[fy]+=cnt[fx]; } } void init(void){ for (int i=1;i<=100099;i++) fa[i]=i; for (int i=1;i<=n;i++) cnt[i]=1; for (int i=n+1;i<=n+64;i++) cnt[i]=0; } void solve(void){ cin>>n; init(); for (int i=1;i<=n;i++){ long long t; cin>>t; for (int j=0;j<64;j++){ if (t>>j&1) merge(i,n+1+j); } } int ans=0; for (int i=n+1;i<=n+64;i++) ans=max(ans,cnt[i]); cout<<ans<<endl; } int main() { Trd; return 0; }
并查集的维护,为每个二进制位创建一个虚拟节点
当 \(w_i\ \&\ w_j\ge 1\) 时,它们就在一个集合内,所以按照二进制拆分的顺序,构造 \(64\) 个元素,他们的二进制表示遵循一个规则
都只有一个 \(1\) 并且所在的位都不相同,例如
00...001 00...010 00...100 ........
这些虚拟节点表示 \(w\) 在哪一位上 \(\&\) 后可以构成一个集合
最后计算连通分量即可,注意初始化时,由于虚拟节点不算连通分量,那么应该初始化为 \(0\)
void init(void){ for (int i=1;i<=100099;i++) fa[i]=i;//并查集初始化 for (int i=1;i<=n;i++) cnt[i]=1;//初始化n个真实节点 for (int i=n+1;i<=n+64;i++) cnt[i]=0;//初始化虚拟节点 }
通过位运算,来判断是否在一个集合内
for (int j=0;j<64;j++){//0~63位 if (t>>j&1)//如果&后为1,那么就要合并 merge(i,n+1+j);//合并节点 }
E:
题目大意:给定有 \(n\) 个字符的二进制串 \(s\) ,定义它的自省值为 \(s_1\ or\ s_2\ or\ s_3\ or\ \cdots\ or\ s_n\),对于每次询问区间 \([l,r]\),计算区间内所有连续子串的自省值之和
#include<bits/stdc++.h> #define cintie ios::sync_with_stdio(false);cin.tie(0);cout.tie(0); #define Trd int T;cin>>T;while (T--)solve(); #define LLinf 9e18; #define Iinf 2e9 #define LL long long #define Lc p<<1 #define Rc p<<1|1 #define lc(x) tr[x].ch[0] #define rc(x) tr[x].ch[1] using namespace std; int n,q; string s; long long sum[200010]; int main() { cin>>n>>s>>q; s='1'+s; s+='1'; long long cnt=0; long long sm=0; set<int> st; for (int i=0;i<=n+1;i++){ if (s[i]=='0') cnt++; else{ st.insert(i); sm+=cnt*(cnt+1)/2; cnt=0; } sum[i]=sm; } while (q--){ long long l,r; cin>>l>>r; long long lt=*st.lower_bound(l); long long rt=*prev(st.upper_bound(r)); if (rt<lt){ cout<<0<<endl; continue; } long long ans=sum[rt]-sum[lt]; ans+=(lt-l)*(lt-l+1)/2+(r-rt)*(r-rt+1)/2; cout<<(r-l+1)*(r-l+2)/2-ans<<endl; } return 0; }
\(\&\) 计算,只有 \(0\ \&\ 0\) 时才为 \(0\) ,题目要求计算总数,那么答案可以被表示为连续子串总数减去为 \(0\) 的连续子串数
记区间 $[l,r] $ 的长度为 \(len\) ,那么答案为
平凡的,例如二进制串 \(s=00100100\),给出的 \([l,r]=[1,7]\),所需要计算的 \(\rm{sumof}(0)\) 为
其中 \(mid\) 段可以通过预处理前缀和得到,\(left,right\) 也可以通过边界计算得出
s='1'+s;//添加左边界哨兵 s+='1';//添加右边界哨兵 long long cnt=0;//用来记录有几个连续的0 long long sm=0;//前缀贡献 set<int> st; for (int i=0;i<=n+1;i++){ if (s[i]=='0') cnt++;//如果为0,那么就累加0的数量 else{//如果为1 st.insert(i);//记录这个1的位置 sm+=cnt*(cnt+1)/2;//累加两个1之间0的贡献 cnt=0;//重置 } sum[i]=sm;//记录贡献 }
给出 l,r
后,需要分为三步计算
cin>>l>>r; long long lt=*st.lower_bound(l);//找第一个小于等于l的值,即前面预处理记录的位置(区间内第一个1) long long rt=*prev(st.upper_bound(r));//找区间内最后一个1的位置 if (rt<lt){//如果最后一个1比第一个1还靠前,说明区间全为0 cout<<0<<endl; continue; } long long ans=sum[rt]-sum[lt];//计算前后两个1之间的0的贡献 ans+=(lt-l)*(lt-l+1)/2+(r-rt)*(r-rt+1)/2;//计算左右截断区间的贡献 cout<<(r-l+1)*(r-l+2)/2-ans<<endl;//计算答案
注意的 prev(it)
函数的作用为将迭代器 it
向前移动一位,即指向 it
的前一个元素
在上例中,prev(st.upper_bound(r))
指向 r-1
,即最后一个 小于等于 r
的 1
的位置。
s='1'+s;//添加左边界哨兵 s+='1';//添加右边界哨兵
添加哨兵的作用为防止序列全 \(0\) ,查找时越界
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· AI技术革命,工作效率10个最佳AI工具