重新振作第三天----NBUACMer-Beginnerround10.18题解

题目链接:https://ac.nowcoder.com/acm/contest/93542#question

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();
}
posted @   菜dog的日常生活  阅读(20)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 全网最简单!3分钟用满血DeepSeek R1开发一款AI智能客服,零代码轻松接入微信、公众号、小程
· .NET 10 首个预览版发布,跨平台开发与性能全面提升
· 《HelloGitHub》第 107 期
· 全程使用 AI 从 0 到 1 写了个小工具
· 从文本到图像:SSE 如何助力 AI 内容实时呈现?(Typescript篇)
点击右上角即可分享
微信分享提示