牛客小白月賽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")<<"\n"; } 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<<"\n"; } 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<<"\n"; } 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<<"\n"; 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?"\n":""); else cout<<"impossible\n"; } 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?"\n":""); } else cout<<"impossible\n"; } 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; }