2020牛客寒假算法基础训练营3题解

写在前面:这场的题目感觉比前两场的难,最后苟了个七题,BEJ估计明天再补吧。现在更新一下已经过了的七道题。

A-牛牛的DRB迷宫I

题意:给出一个nmn*m的迷宫,每一格有三种情况,分别是DD向下移动,RR向右移动,BB可以向下也可以向右移动。
题解:显然的dp题。当a[i][j]=Da[i][j]=Ddp[i+1][j]+=dp[i][j]dp[i+1][j]+=dp[i][j],当a[i][j]=Ra[i][j]=Rdp[i][j+1]+=dp[i][j]dp[i][j+1]+=dp[i][j],当a[i][j]=Ba[i][j]=Bdp[i][j+1]+=dp[i][j],dp[i+1][j]+=dp[i][j]dp[i][j+1]+=dp[i][j],dp[i+1][j]+=dp[i][j]。最后输出dp[n][m]dp[n][m]即可。

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn=1e5+10;
const int mod=1e9+7;
const int INF=0x3f3f3f3f;
ll read(){
    ll f=1,x=0;char ch;
    do{ch=getchar();if(ch=='-')f=-1;}while(ch<'0'||ch>'9');
    do{x=x*10+ch-'0';ch=getchar();}while(ch>='0'&&ch<='9');
    return f*x;
}
ll f[100][100];
int n,m;
char s[100][100];
int main(){
    scanf("%d%d",&n,&m);
    for(int i=0;i<n;i++)scanf("%s",&s[i]);
    f[0][0]=1ll;
    for(int i=0;i<n;i++)
        for(int j=0;j<m;j++)
            if(s[i][j]=='D')f[i+1][j]=(f[i+1][j]+f[i][j])%mod;
            else if(s[i][j]=='R')f[i][j+1]=(f[i][j+1]+f[i][j])%mod;
            else if(s[i][j]=='B')f[i+1][j]=(f[i+1][j]+f[i][j])%mod,f[i][j+1]=(f[i][j+1]+f[i][j])%mod;
    printf("%lld\n",f[n-1][m-1]);
    return 0;
}

C-牛牛的数组越位

题意:题目描述了一下数组越界的概念,具体自己看题目介绍吧。
题解:按照题意模拟即可。

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn=2e7+10;
const int mod=1e9+7;
const int INF=0x3f3f3f3f;
ll read(){
    ll f=1,x=0;char ch;
    do{ch=getchar();if(ch=='-')f=-1;}while(ch<'0'||ch>'9');
    do{x=x*10+ch-'0';ch=getchar();}while(ch>='0'&&ch<='9');
    return f*x;
}
int n,m,p;
int G[1010][1010];
int main(){
    int T;
    scanf("%d",&T);
    while(T--){
        scanf("%d%d%d",&n,&m,&p);
        int fflag=0,ffflag=0;
        for(int i=0;i<n;i++)
            for(int j=0;j<m;j++)G[i][j]=0;
        while(p--){
            int x,y,z;
            scanf("%d%d%d",&x,&y,&z);
            int k=m*x+y;
            if(!ffflag){
                if(k<0 || k>=n*m){ffflag=1;continue;}
                if(x<0 || y<0 || x>=n || y>=m){fflag=1;}
                int yy=k%m,xx=k/m;
                G[xx][yy]=z;
            }
        }
        if(ffflag){
            puts("Runtime error");
            continue;
        }
        for(int i=0;i<n;i++){
            for(int j=0;j<m-1;j++)printf("%d ",G[i][j]);
            printf("%d\n",G[i][m-1]);
        }
        if(fflag)puts("Undefined Behaviour");
        else puts("Accepted");
    }
    return 0;
}

D-牛牛与二叉树的数组存储

题意:题目描述了一下数组存储二叉树的概念,具体自己看题目介绍即可。
题解:按照题意模拟即可。

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn=1e5+10;
const int mod=1e9+7;
const int INF=0x3f3f3f3f;
ll read(){
    ll f=1,x=0;char ch;
    do{ch=getchar();if(ch=='-')f=-1;}while(ch<'0'||ch>'9');
    do{x=x*10+ch-'0';ch=getchar();}while(ch>='0'&&ch<='9');
    return f*x;
}
int n,a[2*maxn],f[maxn*2],sum;
int main(){
    scanf("%d",&n);
    for(int i=1;i<=n;i++)scanf("%d",&a[i]),f[a[i]]=i;
    for(int i=1;i<=n;i++)if(a[i]!=-1)sum++;
    for(int i=n+1;i<=n*2+1;i++)a[i]=-1;
    a[0]=-1;
    printf("The size of the tree is %d\n",sum);
    printf("Node %d is the root node of the tree\n",a[1]);
    for(int i=1;i<=sum;i++){
        printf("The father of node %d is %d, the left child is %d, and the right child is %d\n",i,a[f[i]/2],a[f[i]*2],a[f[i]*2+1]);
    }
    return 0;
}

F-牛牛的Link Power I

题意:给出一个长度为n的01串,设s[u]=s[v]=1s[u]=s[v]='1',则答案需要加上dis(uv)dis(u-v),算出所有的1对对答案的贡献即可。
题解:可以乱搞,方法很多。开一个数组记录所有1的位置,记录一下所有的和sum,再用一个cnt表示当前有多少个1,每次对答案的贡献就是当前的a[i]*cnt-sum。

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn=1e5+10;
const int mod=1e9+7;
const int INF=0x3f3f3f3f;
ll read(){
    ll f=1,x=0;char ch;
    do{ch=getchar();if(ch=='-')f=-1;}while(ch<'0'||ch>'9');
    do{x=x*10+ch-'0';ch=getchar();}while(ch>='0'&&ch<='9');
    return f*x;
}
int n,cnt,num;
ll sum,ans;
int q[maxn];
char a[maxn];
int main(){
    scanf("%d",&n);
    scanf("%s",&a);
    for(int i=0;i<n;i++)
        if(a[i]=='1'){
            q[++cnt]=i;
            sum=sum+1ll*i;
        }
    for(int i=cnt;i>0;i--){
        sum=sum-1ll*q[i];num++;
        ans=(ans+1ll*(cnt-num)*q[i]-sum)%mod;
    }
    printf("%lld\n",ans);
    return 0;
}

H-牛牛的k合因子数

题意:一个正整数,如果有k个合数因子则被称为k合因子数。m个询问,每次问1~n中有多少个k合因子数。
题解:筛法。先筛出1e5全部的素数,然后再用筛法类似的累加一下贡献即可。

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn=1e5+10;
const int mod=1e9+7;
const int INF=0x3f3f3f3f;
ll read(){
    ll f=1,x=0;char ch;
    do{ch=getchar();if(ch=='-')f=-1;}while(ch<'0'||ch>'9');
    do{x=x*10+ch-'0';ch=getchar();}while(ch>='0'&&ch<='9');
    return f*x;
}
int f[maxn],ans[maxn],a[maxn];
int n,m,k;
int main(){
    for(int i=2;i<=100000;i++)
        if(!f[i])
            for(int j=i*2;j<=100000;j+=i)f[j]++;
    for(int i=2;i<=100000;i++)
        if(f[i])
            for(int j=i;j<=100000;j+=i)a[j]++;
//  printf("%d\n",a[20]);
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;i++)ans[a[i]]++;
    while(m--){
        scanf("%d",&k);
        printf("%d\n",ans[k]);
    }
    return 0;
}

G-牛牛的Link Power II

题意:F题题意,但是带修。保证一定是将1改成0,0改成1。每次输出修改后的总答案。
题解:先计算出答案,对于维护我们使用线段树,维护区间1的个数和1的位置总和。若将0变1相当于增加一波贡献,若将1变0相当于减去一波贡献。假设k1和k2分别是[1,pos-1],[pos+1,n]的区间结点。对于答案就应该分别加上或减去posk1.numk1.sum+k2.sumposk2.numpos*k1.num-k1.sum+k2.sum-pos*k2.num

#include "stdio.h"
#include "string.h"
#include "iostream"
#include "algorithm"
#include "stack"
using namespace std;
typedef long long ll;
const int maxn=1e5+10;
const ll mod=1e9+7;
const int INF=0x3f3f3f3f;
ll read(){
    ll f=1,x=0;char ch;
    do{ch=getchar();if(ch=='-')f=-1;}while(ch<'0'||ch>'9');
    do{x=x*10+ch-'0';ch=getchar();}while(ch>='0'&&ch<='9');
    return f*x;
}
struct Node{
    ll sum;
    int num;
}s[maxn*4],res;
stack<int>q;
struct arr{
    int pos,val;
}a[maxn];
Node cmp(Node a,Node c){
    Node k;
    k.num=(a.num%mod+c.num%mod)%mod;
    k.sum=(a.sum%mod+c.sum%mod)%mod;
    return k;
}
void pushup(int node){
    s[node].sum=(s[node<<1].sum%mod+s[node<<1|1].sum%mod)%mod;
    s[node].num=(s[node<<1].num%mod+s[node<<1|1].num%mod)%mod;
    return;
}
void build(int l,int r,int node){
    if(l==r){
        s[node].num=a[l].pos;
        s[node].sum=a[l].val;
        return;
    }
    int mid=(l+r)>>1;
    build(l,mid,node<<1);
    build(mid+1,r,node<<1|1);
    pushup(node);
}
void update(int L,int x,int l,int r,int node){
    if(l==r){
        if(x==1)s[node].sum=L;
        if(x==0)s[node].sum=0;
        s[node].num=x;
        return;
    }
    int mid=(l+r)>>1;
    if(L<=mid)update(L,x,l,mid,node<<1);
    else update(L,x,mid+1,r,node<<1|1);
    pushup(node);
}
Node query(int L,int R,int l,int r,int node){
    if(l>=L && r<=R)return s[node];
    int mid=(l+r)>>1;
    Node res;res.num=0;res.sum=0;
    if(L<=mid) res=cmp(res,query(L,R,l,mid,node<<1));
    if(R>mid)  res=cmp(res,query(L,R,mid+1,r,node<<1|1));
    return res;
}
char t[maxn];
int n,m,cnt;
ll ans,sum;
int main(){
    scanf("%d",&n);
    scanf("%s",&t);
    scanf("%d",&m);
    for(int i=0;i<n;i++){
        if(t[i]=='0')continue;
        sum=(sum+1ll*i)%mod;
        cnt++;
        q.push(i);
        a[i+1].pos=1;
        a[i+1].val=i+1;
    }
    while(!q.empty()){
        int k=q.top();q.pop();
        sum-=k*1ll;
        cnt--;
        ans=(ans+1ll*cnt*k-sum)%mod;
    }
    build(1,n,1);
    printf("%lld\n",ans);
    while(m--){
        int opt,pos;
        scanf("%d%d",&opt,&pos);
        if(opt==1){
            update(pos,1,1,n,1);
            Node k1,k2;
            k1.sum=0ll,k1.num=0;
            k2.sum=0ll,k2.num=0;
            if(pos>1)k1=query(1,pos-1,1,n,1);
            if(pos<n)k2=query(pos+1,n,1,n,1);
            ans=(ans+1ll*pos*k1.num%mod-k1.sum%mod+k2.sum%mod-1ll*pos*k2.num%mod+2*mod)%mod;
        }
        else {
            update(pos,0,1,n,1);
            Node k1,k2;
            k1.sum=0ll,k1.num=0;
            k2.sum=0ll,k2.num=0;
            if(pos>1) k1=query(1,pos-1,1,n,1);
            if(pos<n) k2=query(pos+1,n,1,n,1);
            ans=(ans-1ll*pos*k1.num%mod+k1.sum%mod-k2.sum%mod+1ll*pos*k2.num%mod+2*mod)%mod;
        }
        printf("%lld\n",ans);
    }
    return 0;
}

I-牛牛的汉诺塔

题意:汉诺塔,问A柱到B柱,A柱到C柱,B柱到A柱,B柱到C柱,C柱到A柱,C柱到B柱,以及总共会移动多少次。
题解:拿暴力程序打一下表,可以轻松的地发现总和次数为2n12^n-1,A->B和B->C满足一个规律,B->A和C->B满足一个规律,A->C和C->A分别是个规律,打表出来去OEIS上翻一翻就过了。

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn=1e5+10;
const int mod=1e9+7;
const int INF=0x3f3f3f3f;
ll read(){
    ll f=1,x=0;char ch;
    do{ch=getchar();if(ch=='-')f=-1;}while(ch<'0'||ch>'9');
    do{x=x*10+ch-'0';ch=getchar();}while(ch>='0'&&ch<='9');
    return f*x;
}
ll a,b,c,d,e,f,sum;
ll quick(ll a,ll b){
    ll res=1;
    while(b){
        if(b&1)res=res*a;
        a=a*a;
        b>>=1;
    }
    return res;
}
int n;
int main(){
    scanf("%d",&n);
    sum=quick(2,n)-1;
    if(n==1)b=1;
    if(n>1){
        ll t=n/2-1;
        a=3*t+1;
        a=(a+quick(2,2*t+3))/9;
        d=a;
    }
    if(n>2){
        ll t=(n-1)/2;
        for(int i=1;i<=t;i++)
            c=4*f+i,f=c;
        f=c;
    }
    if(n>3){
        ll t=(n-2)/2;
        e=2*(quick(4,t+1)-3*t-4)/9;
    }
    b=sum-a-c-d-e-f;
    printf("A->B:%lld\n",a);
    printf("A->C:%lld\n",b);
    printf("B->A:%lld\n",c);
    printf("B->C:%lld\n",d);
    printf("C->A:%lld\n",e);
    printf("C->B:%lld\n",f);
    printf("SUM:%lld\n",sum);
    return 0;
}

eg:剩下B题构造,E题数位dp,J题最短路明天补!

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章