題目連結:https://ac.nowcoder.com/acm/contest/93542#question
- A:至至子的斐波那契 (預處理+二分)
- B:愛吃素 (數學)
- C:皇城PK (思維)
- D:牛牛愛幾何 (簡單計算幾何)
- E:小紅的小紅走矩陣 (構造)
- F:遊戲購買! (BFS)
- G:糟糕的打譜員 (dp)
- H:牛牛衝鑽五 (模擬)
- I:我不是酸菜魚 (思維)
- J:四面體 (立體幾何)
- K:異或和之和 (位運算+數學)
- L:完全圖 (思維)
- M:牛牛走迷宮 (思維)
- N:瘋狂的自我檢索者 (思維)
- O:小紅的環形字串 (思維)
A:至至子的斐波那契 (預處理+二分)
題意介紹:有T組查詢,每組查詢給出一個正整數a,要求找出斐波那契數列中,距離a的距離最近的一項的下標,如果距離相同,則輸出下標較小的。
資料範圍: a<=1E18
思路:
先預處理出斐波那契數列,然後每次透過二分找到大於等於a的第一個數的下標p,那麼p-1就是第一個剛好小於a的數的下標,比較兩個數字與a之間的距離即可。
複雜度:O(Tlog(n))
程式碼實現:
#include<bits/stdc++.h>
using namespace std;
#define int long long
#define ios ios::sync_with_stdio(0);cin.tie(0);cout.tie(0)
int n,k;
int fib[1000],cnt=0;
void init(){
fib[1]=1;
for(int i=2;;i++){
fib[i]=fib[i-1]+fib[i-2];
if(fib[i]>1E18){
cnt=i;
break;
}
}
}
void solve(){
int x;
cin>>x;
int p=lower_bound(fib+1,fib+cnt,x)-fib;
if((fib[p]-x)>=(x-fib[p-1])){
cout<<p-1<<endl;
}
else cout<<p<<endl;
}
signed main(){
ios;
init();
int t=1;
cin>>t;
while(t--)
solve();
}
B:愛吃素 (數學)
題意介紹:給出T組查詢,每組查詢給出兩個數字a和b,判斷a*b是否為質數。
資料範圍:a<1E11 b<1E11
思路:兩個數相乘為質數的可能只能是其中一個數字為1,另一個數字為質數。由此,我們只有判斷是否存在其中一個數字為1,另一個數字為字數即可。
複雜度:O(T*sqrt(n))
程式碼實現:
#include<bits/stdc++.h>
using namespace std;
#define int long long
#define ios ios::sync_with_stdio(0);cin.tie(0);cout.tie(0)
bool is_prime(int n){
if(n<2)return false;
for(int i=2;i<=n/i;i++){
if(n%i==0)
return false;
}
return true;
}
int n,m;
void solve(){
cin>>n>>m;
if(is_prime(n)&&m==1||is_prime(m)&&n==1){
cout<<"YES"<<endl;
}
else cout<<"NO"<<endl;
}
signed main(){
ios;
int t=1;
cin>>t;
while(t--)
solve();
}
C:皇城PK (思維)
題意介紹:有T組查詢,每一組查詢先給出兩個正整數n和m,代表n個選手和m局比賽。之後m行輸入兩個整數a,b,代表選手a打敗了選手b。一旦有人有了敗局,則他一定不可能是冠軍。求有多少個選手有可能獲得冠軍。
資料範圍: n<1E5 m<1E5
思路:統計每一個人的敗局數,一旦有了敗局,則沒有可能冠軍。如果沒有敗局,則可能成為冠軍。
複雜度:O(T(n+m))
程式碼實現:
#include<bits/stdc++.h>
using namespace std;
#define int long long
#define ios ios::sync_with_stdio(0);cin.tie(0);cout.tie(0)
const int mod=1E9+7;
int n,m,k;
void solve(){
cin>>n>>m;
int a[n+1];
memset(a,0,sizeof a);
for(int i=1;i<=m;i++){
int u,v;
cin>>u>>v;
a[v]++;
}
int ans=0;
for(int i=1;i<=n;i++){
if(a[i])ans++;
}
cout<<n-ans<<endl;
}
signed main(){
ios;
int t=1;
//cin>>t;
while(t--)
solve();
}
D:牛牛愛幾何 (簡單計算幾何)
題意介紹:有多組輸入,每組輸入正方形的邊長n,每條圓弧以正方形邊長為直徑,求下面這個陰影部分的面積。
資料範圍: n<1E7
思路: 兩個圓形減去正方形即可。
複雜度:O(1)
程式碼實現:
#include<bits/stdc++.h>
using namespace std;
#define int long long
#define ios ios::sync_with_stdio(0);cin.tie(0);cout.tie(0)
const double pi=acos(-1);
int n,k;
void solve(){
double x;
while(cin>>x)
cout<<fixed<<setprecision(6)<<(x/2)*(x/2)*pi*2-x*x<<endl;
}
signed main(){
ios;
int t=1;
//cin>>t;
while(t--)
solve();
}
E:小紅的小紅走矩陣 (構造)
題意介紹:太繁瑣了,懶得概括,因為這題涉及到另外一道題目的題幹,有興趣的可以自己查。
資料範圍: 3<=n,m<=1E3
思路:先根據行列來生成字母,這樣的話是每一個都可以走的。接著我們加以限制,使得1到n-1行的1,2兩列,沒有辦法走,只能走第n行,這樣只需mp[i][1]=mp[i][2]即可。然後我們不難判斷,如果步數最少的話,那就是直接往右走到底,但是這樣的話,答案就是n+m-2。我們只能多走幾步。但是他多走了就得回來,所以增加的是偶數。我們不妨使得最後的結果是n+m,那麼就需要讓他在多走兩步,即mp[n][m-1]=mp[n][m]。
複雜度:O(nm)
程式碼實現:
#include<bits/stdc++.h>
using namespace std;
const int maxn=1e3+5;
int n,m;
char mp[maxn][maxn];
int main(){
cin>>n>>m;
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++)
mp[i][j]=(i+j)%26+'a';
for(int i=1;i<n;i++)mp[i][2]=mp[i][1];
mp[n][m-1]=mp[n][m];
for(int i=1;i<=n;i++)cout<<mp[i]+1<<endl;
return 0;
}
F:遊戲購買! (BFS)
題意介紹:首先給出n和m表示地圖大小,然後給定一個x代表刺激度指標。接下來給出兩個座標(sx,sy)和(ex,ey),代表起點和終點。然後輸入n行,每行m個數字,表示地圖上每個位置的刺激度。如果值為-1,則代表無法通行。求從終點前往起點,且經過的點存在刺激度大於x的最短距離,如果無法實現則輸出-1。
資料範圍: 1<=n,m<=2000
思路:從起點bfs一遍,再從終點bfs一遍,分別記錄到達的最短距離。然後遍歷整個矩陣,將刺激度大於x的值的地方的兩次bfs相加求最小值即可。
複雜度:O(n*m)
程式碼實現:
#include<bits/stdc++.h>
using namespace std;
int main(){
int n,m,x;
cin>>n>>m>>x;
int sx,sy,ex,ey;
cin>>sx>>sy>>ex>>ey;
vector<vector<int>>maze(n+1,vector<int>(m+1,0));
for(int i=1;i<=n;i++){
for(int j=1;j<=m;j++){
cin>>maze[i][j];
}
}
vector<vector<int>>vis(n+1,vector<int>(m+1,0));
vector<vector<int>>vis1(n+1,vector<int>(m+1,0));
queue<pair<int,int>>q;
q.push({sx,sy});
int dx[4]={0,0,-1,1};
int dy[4]={1,-1,0,0};
while(q.size()){
int x=q.front().first;
int y=q.front().second;
q.pop();
for(int i=0;i<4;i++){
if(x+dx[i]>=1&&dx[i]+x<=n&&y+dy[i]<=m&&y+dy[i]>=1&&!vis[x+dx[i]][y+dy[i]]&&maze[x+dx[i]][y+dy[i]]!=-1){
q.push({x+dx[i],y+dy[i]});
vis[x+dx[i]][y+dy[i]]=vis[x][y]+1;
}
}
}
q.push({ex,ey});
while(q.size()){
int x=q.front().first;
int y=q.front().second;
q.pop();
for(int i=0;i<4;i++){
if(x+dx[i]>=1&&dx[i]+x<=n&&y+dy[i]<=m&&y+dy[i]>=1&&!vis1[x+dx[i]][y+dy[i]]&&maze[x+dx[i]][y+dy[i]]!=-1){
q.push({x+dx[i],y+dy[i]});
vis1[x+dx[i]][y+dy[i]]=vis1[x][y]+1;
}
}
}
int ans=99999999;
for(int i=1;i<=n;i++){
for(int j=1;j<=m;j++){
//cout<<vis[i][j]<<' ';
if(maze[i][j]>x&&vis[i][j]&&vis1[i][j]){
ans=min(vis[i][j]+vis1[i][j],ans);
}
}
//cout<<endl;
}
if(ans!=99999999)
cout<<ans<<endl;
else cout<<-1<<endl;
}
G:糟糕的打譜員 (dp)
題意介紹:有T組測試資料,首先給出一個n,代表了譜子的長度,然後每行輸入兩個數字c[i]和a[i],c[i]代表下棋的人,取值只有0和1。a[i]代表的是位置,取值是1-10。一個序列合法的條件是,先後兩步的執棋人不同,並且下的位置也不同,求最長合法序列。
資料範圍: n<=1E5
思路:典型的dp問題,首先開大小為n的dp陣列,記錄最後一個步驟是下標為i的步驟時的最長合法序列。然後我們再用一個陣列pos[2][10],記錄以兩個人,分別以1-10結尾時的最長的序列長度。然後,我們遍歷序列,對於每一個序列,更新它加在不同位置後面所得到的長度,同時更新dp陣列的值。我們對這個值和ans的值取max即可。
複雜度:O(Tn)
程式碼實現:
#include<bits/stdc++.h>
using namespace std;
void solve(){
int n;
cin>>n;
vector<int>a(n+1,0);
vector<int>c(n+1,0);
for(int i=1;i<=n;i++){
cin>>c[i]>>a[i];
}
vector<vector<int>>pos(2,vector<int>(11,0));
vector<int>dp(n+1,0);
int ans=0;
for(int i=1;i<=n;i++){
for(int j=1;j<=10;j++){
if(a[i]!=j){
dp[i]=max(dp[i],dp[pos[c[i]^1][j]]+1);
}
}
if(dp[i]>dp[pos[c[i]][a[i]]]){
pos[c[i]][a[i]]=i;
}
ans=max(ans,dp[i]);
}
cout<<ans<<endl;
}
int main(){
int t;
cin>>t;
while(t--)
solve();
}
H:牛牛衝鑽五 (模擬)
題意介紹:有n組測試案例,每組測試案例先給出兩個數字n和k,分別代表比賽場數和連勝獎勵的星數。接著給出一個由W和L組成的字串,表示比賽情況,其中W代表win,L代表lose。
資料範圍: 3<=n<=1E5 2<=k<=100
思路:設定一個計數器x,每次獲勝則x++,當x>=3的時候,ans+3,否則ans+1。當輸的時候,則ans--,同時x重置為0即可。
複雜度:O(Tn)
程式碼實現:
#include<bits/stdc++.h>
using namespace std;
#define int long long
#define ios ios::sync_with_stdio(0);cin.tie(0);cout.tie(0)
int n,k;
void solve(){
cin>>n>>k;
string s;
int x=0;
int ans=0;
cin>>s;
for(int i=0;i<n;i++){
if(s[i]=='W'){
x++;
if(x>=3){
ans+=k;
}
else ans++;
}
else {
ans--;
x=0;
}
}
cout<<ans<<endl;
}
signed main(){
ios;
int t=1;
cin>>t;
while(t--)
solve();
}
I:我不是酸菜魚 (思維)
題意介紹:給出n個數字a1到an,這n個數字的積為g,在滿足g%2k=0的情況下,求得最大的k。
資料範圍: n<=5E6 1<=ai<=215
思路:n個數字相乘肯定會超出資料範圍。我們不妨分別求出每一個數字關於2進行質因數分解,求出該數字可以被多少個2整除。之後,在將所有的個數累加,即是最終的答案。
複雜度:O(logn)
程式碼實現:
#include<bits/stdc++.h>
using namespace std;
#define int long long
#define ios ios::sync_with_stdio(0);cin.tie(0);cout.tie(0)
int n,m,k;
void solve(){
cin>>n;
int x,ans=0;
for(int i=0;i<n;i++){
cin>>x;
while(x%2==0){
x/=2;
ans++;
}
}
cout<<ans<<endl;
}
signed main(){
ios;
int t=1;
//cin>>t;
while(t--)
solve();
}
J:四面體 (立體幾何)
題意介紹:給出四面體的四個點,計算四面體的體積。
資料範圍: [-50,50]
思路:純純立體幾何題,空間中六面體的體積,先找一個角的三條邊,然後其中兩條邊叉乘的結果再和第三條邊點乘即可。建議線性代數好好學。
複雜度:O(1)
程式碼實現:
#include <iostream>
#include <cmath>
struct Point {
double x, y, z;
};
// 計算向量差
Point vectorDifference(Point a, Point b) {
Point result;
result.x = a.x - b.x;
result.y = a.y - b.y;
result.z = a.z - b.z;
return result;
}
// 計算向量叉積
Point vectorCrossProduct(Point a, Point b) {
Point result;
result.x = a.y * b.z - a.z * b.y;
result.y = a.z * b.x - a.x * b.z;
result.z = a.x * b.y - a.y * b.x;
return result;
}
double vectorDotProduct(Point a, Point b) {
return a.x * b.x + a.y * b.y + a.z * b.z;
}
// 計算四面體體積
double tetrahedronVolume(Point a, Point b, Point c, Point d) {
Point AB = vectorDifference(b, a);
Point AC = vectorDifference(c, a);
Point AD = vectorDifference(d, a);
Point crossProduct = vectorCrossProduct(AC, AD);
double dotProduct = vectorDotProduct(AB, crossProduct);
return std::abs(dotProduct) / 6.0;
}
int main() {
Point A, B, C, D;
std::cin >> A.x >> A.y >> A.z;
std::cin >> B.x >> B.y >> B.z;
std::cin >> C.x >> C.y >> C.z;
std::cin >> D.x >> D.y >> D.z;
double volume = tetrahedronVolume(A, B, C, D);
std::cout << volume << std::endl;
return 0;
}
K:異或和之和 (位運算+數學)
題意介紹:給出一個長度為n的陣列,求這些數中任取3個數異或運算後求和的值。
資料範圍: 3<=n<=2E5 ai<=1E18
思路:首先,我們是這些數中任意選取,那麼就相當於所有數字組合的情況都會發生。這樣就保證了每一位的獨立性,因為每一位都會與其他數字相同位置上數進行異或,也會遍歷過所有的情況。因此,我們只需要統計這個位置上的0與1的個數即可。然後,三個異或為1才會是有效貢獻,因為如果為0,其實我們也就沒有必要計算,因為對於結果沒有影響。所以只存在兩種情況,1個1和3個1。對於這兩種情況,假設1的個數為x,那麼一個1的情況就是x(n-x)(n-x-1)種,三個1的情況為x(x-1)(x-2)種。每一種的值都是v[i],代表這個位置上的1的權值。然後遍歷不同位置,求和即可。1E18的話最多應該是60個位置。我們就遍歷一遍就行。
複雜度:O(61n)
程式碼實現:
#include<bits/stdc++.h>
using namespace std;
#define int long long
#define ios ios::sync_with_stdio(0);cin.tie(0);cout.tie(0)
const int mod=1E9+7;
int ksm(int n,int m,int mod){
int res=1;
n=n%mod;
while(m){
if(m&1) res=res%mod*n%mod;
n=n*n%mod;
m>>=1;
}
return res%mod;
}
int n,m,k;
int bit[62];
int vv[62]={1};
void solve(){
for(int i=1;i<=61;i++){
vv[i]=vv[i-1]*2%mod;
}
cin>>n;
int x=0;
for(int i=1;i<=n;i++){
cin>>x;
for(int j=0;j<=61;j++){
if(x&1){
bit[j]++;
}
x>>=1;
}
}
int ans=0;
for(int i=0;i<=61;i++){
int x=bit[i];
int y=n-x;
ans=(ans+max(x*(x-1)%mod*(x-2)%mod*ksm(6,mod-2,mod)%mod*vv[i]%mod,0ll)+max(x*(y-1)%mod*y%mod*ksm(2,mod-2,mod)%mod*vv[i]%mod,0ll))%mod;
}
cout<<ans<<endl;
}
signed main(){
ios;
int t=1;
//cin>>t;
while(t--)
solve();
}
L:完全圖 (思維)
題意介紹:有t組資料,每組資料給出n和m,代表一個完全圖的頂點數量以及可以刪掉的邊數。求最多可以有多少個聯通分量。
資料範圍: 1<=n,m<=1E18
思路:要使得一個n頂點的完全圖,多出一個聯通分量,那麼就需要刪掉一個頂點的n-1條邊,再多一個就再刪n-2條邊。我們二分最後一次減去的邊的數量,然後用n減去這個值,即可得到最終的答案。但是這邊有個處理細節,在二分的時候,引數要改為double,因為會爆long long。
複雜度:O(logn)
程式碼實現:
#include<bits/stdc++.h>
using namespace std;
#define int long long
#define ios ios::sync_with_stdio(0);cin.tie(0);cout.tie(0)
int n,m,k;
bool check(long double mid){
if((n-mid)*(mid+n-1)/2>m)
return true;
return false;
}
void solve(){
cin>>n>>m;
int l=0,r=n-1,mid;
while(l<r){
mid=(l+r+1)>>1;
if(check(mid)){
l=mid;
}
else r=mid-1;
}
cout<<n-r<<endl;
}
signed main(){
ios;
int t=1;
cin>>t;
while(t--)
solve();
}
M:牛牛走迷宮 (思維)
題意介紹:給出一個n*m的迷宮,要求從(1,1)走到(n,m)。如果可以走到,就走所需步數最小的那一條,如果有多條步數一樣,那就輸出字典序最小的那一條。輸出路徑含DLRU,代表往哪裡走。
資料範圍: 2<=n,m<=500
思路:以DLRU的順序進行BFS,先抵達的一定是字典序最小的。因為每一層的BFS,所走的步數都是相同的,而我們如果以DLRU的順序,則會將字典序較小的先放入佇列當中。這樣可以確保在第一次到達時,是字典序最小的那條。因為本題的資料較小,我就直接存的是字串。如果資料大的時候,建議使用遞迴輸出路徑。
複雜度:O(nm)
程式碼實現:
#include<bits/stdc++.h>
using namespace std;
int main(){
int n,m;
cin>>n>>m;
char a[n+1][m+1];
for(int i=1;i<=n;i++){
for(int j=1;j<=m;j++){
cin>>a[i][j];
//cout<<a[i][j];
}
//cout<<endl;
}
int vis[n+1][m+1];
memset(vis,0,sizeof vis);
//cout<<vis[1][2]<<endl;
queue<pair<pair<int,int>,string>>q;
q.push({{1,1},""});
int dx[4]={1,0,0,-1};
int dy[4]={0,-1,1,0};
char ds[4]={'D','L','R','U'};
vis[1][1]=1;
//int cnt=0;
while(q.size()){
int x=q.front().first.first;
int y=q.front().first.second;
string s=q.front().second;
//cout<<s<<endl;
//cnt++;
//cout<<cnt<<endl;
//cout<<s<<endl;
if(x==n&&y==m){
cout<<s.size()<<endl;
cout<<s<<endl;
return 0;
}
q.pop();
//cout<<n<<' '<<m<<endl;
for(int i=0;i<4;i++){
//cout<<x+dx[i]<<' '<<y+dy[i]<<endl;
// cout<<s<<' '<<ds[i]<<endl;
if(x+dx[i]>=1&&y+dy[i]<=m&&x+dx[i]<=n&&y+dy[i]>=1&&!vis[x+dx[i]][y+dy[i]]&&a[x+dx[i]][y+dy[i]]=='0'){
//cout<<i<<' '<<s+ds[i]<<endl;
//cout<<x+dx[i]<<' '<<y+dy[i]<<endl;
vis[x+dx[i]][y+dy[i]]=1;
//cout<<x+dx[i]<<' '<<y+dy[i]<<endl;
q.push({{x+dx[i],y+dy[i]},s+ds[i]});
}
}
}
cout<<-1<<endl;
}
N:瘋狂的自我檢索者 (思維)
題意介紹:首行輸入n,m兩個數字,分別代表n個評委和m個隱藏分數的人。然後輸入n-m個分數,每個分數都在1-5之間。要求最大可能得分和最小可能得分。
資料範圍: 1<= m <= n<=2E5
思路:最大可能得分就是隱藏分數的人都給最大分,最小可能得分就是隱藏分數的人都給最小分。
複雜度:O(n-m)
程式碼實現:
#include<bits/stdc++.h>
using namespace std;
#define int long long
#define ios ios::sync_with_stdio(0);cin.tie(0);cout.tie(0)
const double pi=acos(-1);
int n,m,k;
void solve(){
cin>>n>>m;
int sum=0,x;
for(int i=1;i<=n-m;i++){
cin>>x;
sum+=x;
}
cout<<fixed<<setprecision(6)<<(sum+m)*1.0/n<<' '<<(sum+m*5)*1.0/n<<endl;
}
signed main(){
ios;
int t=1;
//cin>>t;
while(t--)
solve();
}
O:小紅的環形字串 (思維)
題意介紹:給出兩個字串,一個是s,一個是t。其中s是一個環,求從s中擷取一段連續字串等於字串t的方案數有多少。
資料範圍: 1<=len(t)<=len(s)<=1000
思路:為了解決環形的問題,直接把第一個字串複製兩次,然後直接暴力即可。(substr複雜度為O(n))
複雜度:O(nm)
程式碼實現:
#include<bits/stdc++.h>
using namespace std;
#define int long long
#define ios ios::sync_with_stdio(0);cin.tie(0);cout.tie(0)
int n,k;
void solve(){
string s,t;
cin>>s>>t;
int n=s.size(),m=t.size();
s=s+s;
int ans=0;
for(int i=0;i<n;i++){
string temp=s.substr(i,m);
if(temp==t){
ans++;
//cout<<i<<endl;
}
}
cout<<ans<<endl;
}
signed main(){
ios;
int t=1;
//cin>>t;
while(t--)
solve();
}