2020牛客寒假算法基础集训营1 题解(部分)

比赛地址:https://ac.nowcoder.com/acm/contest/3002
比赛来源:2020牛客寒假算法基础集训营1

A honoka和格点三角形

  思路:首先是样例中给出的 6 种三角形,其次还有 4 种三角形如下图:
图片说明

#include<bits/stdc++.h>
using namespace std;

typedef long long ll;
const int mod=1e9+7;

int main(){
    int n,m; scanf("%d%d",&n,&m); 
    ll ans;    
    ans=((1ll*(m-2)*(n-1)*6)%mod+(1ll*(n-2)*(m-1)*6)%mod)%mod;
    ans=(ans+((((1ll*(n-2)*(m-1))%mod)*(m-2))%mod)*2)%mod;//非特殊
    ans=(ans+((((1ll*(m-2)*(n-1))%mod)*(n-2))%mod)*2)%mod;//非特殊 
    ans=(ans+((((1ll*(n-1)*(m-2))%mod)*(m-3))%mod)*2)%mod;//非特殊 
    ans=(ans+((((1ll*(m-1)*(n-2))%mod)*(n-3))%mod)*2)%mod;//非特殊 
    printf("%lld\n",ans);
    return 0;
}

B kotori和bangdream

  思路:签到题,直接计算即可。

#include<bits/stdc++.h>
using namespace std;

int main(){
    double n,x,a,b; scanf("%lf%lf%lf%lf",&n,&x,&a,&b);
    double ans=(x*a+(100-x)*b)*n/100;
    printf("%.2f\n",ans);
    return 0;
} 

D hanayo和米饭

   直接记录出现的数字,遍历查找未出现过得数字。

#include<bits/stdc++.h>
using namespace std;

const int maxn=1e5+10;
int a[maxn];
map<int,int>m;

int main(){
    int n; scanf("%d",&n);
    for(int i=1;i<=n-1;i++){
        scanf("%d",&a[i]);
        m[a[i]]++;
    }
    for(int i=1;i<=n;i++){
        if(!m[i]){
            printf("%d\n",i);
            break;
        }
    }
    return 0;
} 

E rin和快速迭代

  思路:模板题,查找因子并且记录个数,注意点见代码。

#include<bits/stdc++.h>
using namespace std;

typedef long long ll;

ll solve(ll n){
    ll cnt1=0;
    while(n!=2){
        ll cnt=0; cnt1++;
        for(long long i=1;i*i<=n;i++){//sqrt(n)会出错
            if(n%i==0){
                if(i*i==n) cnt++;
                else cnt+=2;
            }
        }
        n=cnt;
    }
    return cnt1;
}

int main(){
    ll n; scanf("%lld",&n);
    ll cnt=solve(n);
    printf("%lld\n",cnt);
    return 0;
} 

F maki和tree

  思路:满足题意的情况为黑点在路径中间和黑点是端点的情况,我们用并查集可以先预处理出每一个白点连通块并且记录每一个连通块中白点的数量。黑点是端点的情况我们直接加与这个点连接的连通块的白点的个数,在中间的情况见下图。
图片说明
2 和 3 到 1 的路径,3 到 2 的路径。

#include<bits/stdc++.h>
using namespace std;

typedef long long ll;
const int maxn=1e5+10;
char str[maxn];
int cnt=0,head[maxn],f[maxn],size[maxn];

struct Edge{
    int u,v,next;
}edge[maxn<<1];

void addedge(int u,int v){
    edge[cnt].u=u;
    edge[cnt].v=v;
    edge[cnt].next=head[u];
    head[u]=cnt++;
}

int Find(int v){
    if(f[v]==v) return v;
    else return f[v]=Find(f[v]);    
}

void Merge(int u,int v){
    int t1=Find(u),t2=Find(v);
    if(t1!=t2){
        f[t1]=t2;//t1的父节点为t2 
        size[t2]+=size[t1];
    }
}

int main(){
    int n; scanf("%d",&n);
    scanf("%s",str+1);
    for(int i=1;i<=n;i++){
        size[i]=(str[i]=='W'); 
        f[i]=i; head[i]=-1;
    }
    for(int i=1;i<=n-1;i++){
        int u,v; scanf("%d%d",&u,&v);
        addedge(u,v); addedge(v,u);
        if(str[u]==str[v]&&str[u]=='W') Merge(u,v);//预处理出所有的白色连通块 
    }
    //for(int i=1;i<=n;i++) cout<<size[i]<<" "; cout<<endl;
    ll sum=0;
    for(int i=1;i<=n;i++){
        if(str[i]=='B'){
            ll cnt1=0;
            for(int j=head[i];j!=-1;j=edge[j].next) cnt1+=(ll)size[Find(edge[j].v)];//端点 
            sum+=cnt1;
            for(int j=head[i];j!=-1;j=edge[j].next){//中间 
                cnt1-=(ll)size[Find(edge[j].v)];
                sum+=1ll*cnt1*size[Find(edge[j].v)];
            }
        }
    } 
    printf("%lld\n",sum);
    return 0;
}

G eli和字符串

  思路:直接二分长度,然后判断此长度下的子字符串中每一个字符出现的次数。如果满足题意,那么再找是否有更短的字符串;若不满足题意,就找更长的字符串。

#include<bits/stdc++.h>
using namespace std;

const int maxn=2e5+10;
char str[maxn];
int cnt[30],n,k;

bool check(int len){
    for(int i=0;i<30;i++) cnt[i]=0;
    for(int i=0;i<len;i++){
        cnt[str[i]-'a']++;
        if(cnt[str[i]-'a']==k) return true;
    } 
    for(int i=len;i<n;i++){
        cnt[str[i-len]-'a']--; cnt[str[i]-'a']++;
        if(cnt[str[i]-'a']==k) return true;
    }
    return false;
}

int main(){
    scanf("%d%d",&n,&k);
    scanf("%s",str);
    int l=1,r=n,ans=-1; 
    while(l<=r){
        int mid=l+(r-l)/2;
        if(check(mid)){
            r=mid-1;
            ans=mid;
        }else l=mid+1;
    }
    printf("%d\n",ans);
    return 0;
}

H nozomi和字符串

  思路:同 G ,二分长度,记录此长度下,每一个子串 0 1 的个数,如果小的那个小于等于 k,则满足题意,再找是否存在更长的字符串,否则答案可能更短。

#include<bits/stdc++.h>
using namespace std;

const int maxn=2e5+10;
char str[maxn];
int cnt[2],n,k;

bool check(int len){
    cnt[0]=cnt[1]=0;
    for(int i=0;i<len;i++) cnt[str[i]-'0']++;
    for(int i=0;i<len;i++){
        if(min(cnt[0],cnt[1])<=k) return true;
    }
    for(int i=len;i<n;i++){
        cnt[str[i-len]-'0']--; cnt[str[i]-'0']++;
        if(min(cnt[0],cnt[1])<=k) return true;
    }
    return false;
}

int main(){
    scanf("%d%d",&n,&k);
    scanf("%s",str);
    int l=1,r=n,ans=0;
    while(l<=r){
        int mid=l+(r-l)/2;
        if(check(mid)){
            l=mid+1;
            ans=mid;
        }else r=mid-1;
    }
    printf("%d\n",ans);
    return 0;
}

I nico和niconiconi

  思路: 表示截止到当前字符最大的计分分数。

#include<bits/stdc++.h>
using namespace std;

typedef long long ll;
const int maxn=3e5+20;
ll dp[maxn];
string str;

int main(){
    int n,a,b,c; cin>>n>>a>>b>>c;
    cin>>str;
    for(int i=1;i<n;i++){
        dp[i]=max(dp[i],dp[i-1]);
        if(i>=3&&str.substr(i-3,4)=="nico") dp[i]=max(dp[i],dp[i-3]+(ll)a);
        if(i>=5&&str.substr(i-5,6)=="niconi") dp[i]=max(dp[i],dp[i-5]+(ll)b);
        if(i>=9&&str.substr(i-9,10)=="niconiconi") dp[i]=max(dp[i],dp[i-9]+(ll)c);
    }
    printf("%lld\n",dp[n-1]);
    return 0;
}
全部评论

相关推荐

预计下个星期就能开奖吧,哪位老哥来给个准信
华孝子爱信等:对接人上周说的是这周
点赞 评论 收藏
分享
10-05 23:02
东北大学 Java
我说句实话啊:那时候看三个月培训班视频,随便做个项目背点八股,都能说3 40w是侮辱价
点赞 评论 收藏
分享
点赞 评论 收藏
分享
1 收藏 评论
分享
牛客网
牛客企业服务