牛客小白月赛23
A. 膜法记录
和我签订契约成为魔法少女吧!
题意:n行m列网格中分布着敌人,可以进行a次行blast和b次列blast,问能否全歼敌人 。(T≤105,n≤5,m≤105或T=1,n≤20,m≤105)
思路:n比较小,枚举每行是否选取即可。
#include <bits/stdc++.h> using namespace std; const int M=1e5+100; int n,m,a,b; char Mp[10][M]; bool Biu[10],All_dead; bool OK(){ set<int> col; for(int i=1;i<=n;i++){ if(Biu[i]) continue; for(int j=1;j<=m;j++) if(Mp[i][j]=='*') col.insert(j); } return b>=col.size(); } void DFS(int Row,int Biu_num){ if(Row>n+1||Biu_num>a) return; if(OK()){All_dead=true;return;} Biu[Row]=true; DFS(Row+1,Biu_num+1); Biu[Row]=false; DFS(Row+1,Biu_num); } void solve(){ All_dead=false; fill(Biu,Biu+10,false); cin>>n>>m>>a>>b; for(int i=1;i<=n;i++) for(int j=1;j<=m;j++) cin>>Mp[i][j]; DFS(1,0); cout<<(All_dead?"yes":"no")<<" "; } int main() { int t;cin>>t; while(t--) solve(); return 0; }
B. 阶乘
题意:共T次询问,每次询问给定一个正整数p,求阶乘是p的倍数的最小正整数n。(T≤103,p≤109)
思路:分解质因数,存储每个质因数的个数,将重复的质因数分配到其倍数中。
Tips:p可能为109规模的质数,散列数组一方面内存利用率较低,一方面开不到这个大小,所以使用map存储。取质因数取到根号n即可,注意最后将p也存起来。
#include <bits/stdc++.h> using namespace std; void solve(){ int p;cin>>p; map<int,int> _map; for(int i=2;i*i<=p;i++) while(p%i==0) ++_map[i],p/=i; ++_map[p]; int ans=1; for(auto &i:_map){ int base=i.first,num=i.second; for(int j=1;num>0;j++){ int now=j*base; ans=max(ans,now); while(num>0&&now%base==0) now/=base,--num; } } cout<<ans<<" "; } int main() { int t;cin>>t; while(t--) solve(); return 0; }
C. 完全图
题意:n点完全图去掉m边后的最大连通分量。(T≤104,1≤n≤1018,1≤m≤1018)
思路:逐点去除,第一个点需要n-1条边,第二个点需要n-2条边,求得去掉n个点需要的边与m比较即可。由于数据比较大,所以需要二分查找。
Tips:递推公式的中间值会大于long long,可以将不等式变换到long long运算的范围。
#include <bits/stdc++.h> using namespace std; typedef long long ll; int main() { int t;cin>>t; while(t--){ ll n,m;cin>>n>>m; ll l=0,r=n-1; while(l<r){ ll mid=(l+r+1)>>1;//+1是为了防止l=r-1时的不断循环 if(2*n-mid-1<=2*m/mid) l=mid; else r=mid-1; } cout<<l+1<<" "; } return 0; }
E. A+B问题
题意:A和B都在32位有符号整数能存储的范围内,现在已知二者的和为c,问有多少种可能的输入数据。
思路:很有趣的一道题,从int的底层存储考虑,每个32bit都会对应一个和为特定值的32bit,所以答案始终是4294967296。
#include <bits/stdc++.h> using namespace std; int main() { cout<<4294967296; return 0; }
G. 树上求和
题意:n点n-1边树,为每个边赋权值,使得任意两点间路径的权值和的和最小。
思路:每个边左右点的个数之积即为该边的使用次数,将积从大到小依次赋权即可。
#include <bits/stdc++.h> using namespace std; const int M=1e5+100; int n; vector<int> e[M]; vector<long long> v; int dfs(int u,int p){ int sum=1; for(int v:e[u]) if(v!=p) sum+=dfs(v,u); v.push_back(1LL*sum*(n-sum)); return sum; } int main() { cin>>n; for(int i=0;i<n-1;i++){ int u,v;cin>>u>>v; --u,--v; e[u].push_back(v); e[v].push_back(u); } dfs(0,-1); sort(v.begin(),v.end(),greater<long long>()); long long sum=0,num=1; for(auto i:v) sum+=i*num++; cout<<sum<<" "; return 0; }
H. 奇怪的背包问题增加了
题意:给你一些2的幂次方,问能否凑出230,并输出选取情况。(T≤105,1≤m≤105,Σm≤105,0≤ki<30)
思路:若当前幂次个数足够使用则选取所需,否则选取全部并继续向低幂次寻找。
开始时用递归写的,之后简化成了非递归,前者较易实现,后者较为简洁。
#include <bits/stdc++.h> using namespace std; const int M=1e5+100; int Bit[32],ans[M]; vector<int> pos[32]; bool flag; void need(int bit,int num){ if(bit==-1){ flag=false; return; } if(Bit[bit]<num){ for(int i:pos[bit]) ans[i]=1; need(bit-1,2*(num-Bit[bit])); }else{ for(int i=0;i<num;i++) ans[pos[bit][i]]=1; } } void init(){ flag=true; for(auto &v:pos) v.clear(); fill(Bit,Bit+32,0); fill(ans,ans+M,0); } void solve(){ init(); int n;cin>>n; for(int i=0;i<n;i++){ int t;cin>>t; ++Bit[t]; pos[t].push_back(i); } need(30,1); if(flag) for(int i=0;i<n;i++) cout<<ans[i]<<(i==n-1?" ":""); else cout<<"impossible "; } int main() { int t;cin>>t; while(t--) solve(); return 0; }
#include <bits/stdc++.h> using namespace std; const int M=1e5+100; void solve(){ int Bit[32]={0},ans[M]={0}; vector<int> pos[32]; int n;cin>>n; for(int i=0;i<n;i++){ int t;cin>>t; ++Bit[t]; pos[t].push_back(i); } int bit=30,need=1; while(bit!=-1&&Bit[bit]<need){ need=2*(need-Bit[bit]); for(int i:pos[bit]) ans[i]=1; --bit; } if(bit!=-1){ for(int i=0;i<need;i++) ans[pos[bit][i]]=1; for(int i=0;i<n;i++) cout<<ans[i]<<(i==n-1?" ":""); } else cout<<"impossible "; } int main() { int t;cin>>t; while(t--) solve(); return 0; }
I. 寻找子串
题意:字符串的子串是指字符串中连续的一段,给定字符串s,请你找出字典序最大的子串。(|s|≤103)
思路:先找到字典序最大的字母,然后以该字母为首截取子串,取最大串即可。
#include <bits/stdc++.h> using namespace std; int main() { string s;cin>>s; char c=*max_element(s.begin(),s.end()); string ans; for(int i=0;i<s.size();i++) if(s[i]==c) ans=max(ans,s.substr(i)); cout<<ans; return 0; }
J. 最大的差
题意:给定n个数字,请你从中选出两个数字,使得这两个数字的差尽量大,输出这个最大的差。(2≤n≤105,|ai|≤105)
思路:签到题。
#include <bits/stdc++.h> using namespace std; int main() { int n;cin>>n; int a[n];for(int &i:a) cin>>i; sort(a,a+n); cout<<a[n-1]-a[0]; return 0; }