2020牛客寒假算法基礎訓練營1全部題解及總結

寫在前面:題目確實很基礎,也有坑點和梯度。整場比賽被J題的先後順序關了。導致C和F也沒寫(雖然隊友在我面前表演過了F),樹上的東西確實還是我的短板。下面按照我覺得的難度寫一波題解。

E-rin和快速迭代

題意:給出一個正整數爲n,他的所有因數個數爲x,重複這個過程,直到n爲2.
題解:暴力這個過程即可。

#include "stdio.h"
#include "string.h"
#include "math.h"
#include "iostream"
#include "algorithm"
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
int n,m;
ll a;
int sum,num;
int main(){
    int T;
    scanf("%lld",&a);
    while(a!=2){
        sum=2;num++;
        for(ll i=2;i*i<=a;i++)
            if(a%i==0){
                if(i*i==a)sum++;
                else sum+=2;
            }
        a=sum;
    }
    printf("%d\n",num);
    return 0;
}

D-hanayo和米飯

題意:給出n-1個數,每個數不同且範圍都在1~n,問缺少了哪個數。
題解:map或者數組標記一下即可。

#include "stdio.h"
#include "string.h"
#include "math.h"
#include "iostream"
#include "algorithm"
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
const int maxn=1e5+10;
int n,m,ans;
int a[maxn];
int main(){
    scanf("%d",&n);
    for(int i=1;i<n;i++)scanf("%d",&m),a[m]=1;
    for(int i=1;i<=n;i++)if(!a[i])ans=i;
    printf("%d\n",ans);
    return 0;
}

B-kotori和bangdream

題意:n道題,每道題答對概率爲x%,答對加a分答錯加b分。問最後可能得了多少分。
題解:直接算期望即可。

#include "stdio.h"
#include "string.h"
#include "math.h"
#include "iostream"
#include "algorithm"
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
const int maxn=1e5+10;
int n,x,a,b;
int main(){
    scanf("%d%d%d%d",&n,&x,&a,&b);
    double ans=0;
    ans=1.0*x/100*a+1.0*(100-x)/100*b;
    printf("%.2lf\n",ans*n);
    return 0;
}

G-eil和字符串

題意:給出一個長度爲n的字符串,問至少包含k個相同字符的最短子串的長度爲多少。
題解:存下所有字符所在的位置,相同字符如果小於k個就不考慮了,如果大於等於k個,直接搞個滑窗取個最小值即可。

#include "stdio.h"
#include "string.h"
#include "math.h"
#include "iostream"
#include "algorithm"
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
const int maxn=2e5+10;
const int INF=0x3f3f3f3f;
int n,k;
char s[maxn];
int ch[30][maxn],a[30];
int main(){
    scanf("%d%d",&n,&k);
    scanf("%s",&s);
    for(int i=0;i<n;i++)
        ch[s[i]-'a'+1][++a[s[i]-'a'+1]]=i;
    int ans=INF;
    for(int i=1;i<='z'-'a'+1;i++)
        if(a[i]<k)continue;
        else{
            for(int j=1;j+k-1<=a[i];j++)
                ans=min(ans,ch[i][j+k-1]-ch[i][j]+1);
        }
    if(ans==INF)puts("-1");
    else printf("%d\n",ans);
    return 0;
}

I-nico和niconiconi

題意:給出一個字符串s,nico算a分,niconi算b分,niconiconi算c分,問怎麼數這個字符串使得分數最高。
題解:dp水題。一眼就可以看出狀態轉移方程爲f[i]=max(f[i],f[ilen(nico)]+a,f[ilen(niconi)]+b,f[ilen(niconiconi)]+c)f[i]=max(f[i],f[i-len(nico)]+a,f[i-len(niconi)]+b,f[i-len(niconiconi)]+c),然後直接掃描一遍就完事了。因爲是在剛J途中隨手過的I,代碼寫的有點醜。

#include<stdio.h>
#include<string.h>
#include<iostream>
#include<algorithm>
#include<math.h>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
const int maxn=3e5+10;
int n;
ll a,b,c;
char s[maxn];
ll f[maxn];
int main(){
    scanf("%d%lld%lld%lld",&n,&a,&b,&c);
    scanf("%s",&s);
    for(int i=0;i<n;i++){
        f[i]=i?f[i-1]:0;
        if(i>=3 && s[i-3]=='n' && s[i-2]=='i' && s[i-1]=='c' && s[i]=='o')
            f[i]=max(f[i],(i>=4?f[i-4]:0)+a);
        if(i>=5 && s[i-5]=='n' && s[i-4]=='i' && s[i-3]=='c' && s[i-2]=='o' && s[i-1]=='n' && s[i]=='i')
            f[i]=max(f[i],(i>=6?f[i-6]:0)+b);
        if(i>=9 && s[i-9]=='n' && s[i-8]=='i' && s[i-7]=='c' && s[i-6]=='o' && s[i-5]=='n' && s[i-4]=='i' && s[i-3]=='c' && s[i-2]=='o' && s[i-1]=='n' && s[i]=='i')
            f[i]=max(f[i],(i>=10?f[i-10]:0)+c);
    }
    printf("%lld\n",f[n-1]);
    return 0;
}

A-honoka和格點三角形

題意:n*m的格點矩陣,問至少有一邊與對稱軸平行並且面積爲1的格點三角形有多少個。
題解:數數題真的很難(我覺得),方案也很多。可以先數只有一邊和x軸平行的,再數只有一邊和y軸平行的,減去兩邊都平行的。或者考慮容斥。或者考慮先數底爲1,再數底爲2。等等。

#include "stdio.h"
#include "string.h"
#include "math.h"
#include "iostream"
#include "algorithm"
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
const int maxn=2e5+10;
const int INF=0x3f3f3f3f;
const ll mod=1e9+7;
ll n,m;
int main(){
    scanf("%lld%lld",&n,&m);
    ll ans=0;
    if(n>m)swap(n,m);
    ans=((n-1)*max(m-2,0ll)+max(0ll,n-2)*(m-1))%mod*2*max(0ll,m-2)%mod;
    swap(n,m);
    ans=(ans+((n-1)*(m-2)%mod+max(0ll,n-2)*(m-1)%mod)%mod*2*m%mod)%mod;
    printf("%lld\n",ans);
    return 0;
}

H-nozomi和字符串

題意:長度爲n的01字符串,有k個機會將0變1,將1變0,問怎麼操作使得一個全1子串或者全0子串儘可能長。
題解:存下0和1的所有位置,我們以1爲例。假設所有的1的位置都依次存放在數組a之中。那麼在i這個位置,對答案有貢獻的部分就是a[i+k]-a[i-1],改變的就是i到i+k部分的非0或非1字符。

#include "stdio.h"
#include "string.h"
#include "math.h"
#include "iostream"
#include "vector"
#include "algorithm"
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
const int maxn=2e5+10;
const int INF=0x3f3f3f3f;
const ll mod=1e9+7;
int n,k;
char s[maxn];
int a[2][maxn],b[2];
int main(){
    scanf("%d%d",&n,&k);
    scanf("%s",s+1);
    int ans=1;
    for(int i=1;i<=n;i++)a[s[i]-'0'][++b[s[i]-'0']]=i;
    /*for(int i=0;i<=1;i++){
        for(int j=1;j<=b[i];j++)printf("%d ",a[i][j]);
        printf("\n");
    }*/
    a[0][++b[0]]=n+1;a[1][++b[1]]=n+1;
    for(int i=0;i<=1;i++){
        for(int j=1;j<b[i];j++)
            if(j+k>b[i])ans=max(n-a[i][j-1],ans);
            else ans=max(ans,a[i][j+k]-a[i][j-1]-1);
    }
    if(b[1]==1 || b[0]==1)ans=n;
    printf("%d\n",ans);
    return 0;
}

J-u‘s的影響力

題意F1=x,F2=y,Fn=Fn1Fn2abn>2)F_1=x,F_2=y,F_n=F_{n-1}*F_{n-2}*a^b(n>2)Fnmod(1e9+7)F_nmod(1e9+7)
題解:思路是顯然的,考慮指數的關係即可。可以很輕鬆的發現x和y的指數符合斐波那契數列,假設當n=xn=x時x的指數爲Fn1F_{n-1},y的指數爲FnF_n,則此時a的指數爲(Fn+Fn11)b(F_{n}+F_{n-1}-1)*b 考慮到b可能爲mod-1的倍數,所以要先計算aba^b這也是我一直wa的地方。
update:題目突然加強數據了- -特判一下a x y是否是p的倍數即可

#include<stdio.h>
#include<string.h>
#include<iostream>
#include<algorithm>
#include<math.h>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
struct arr{
    ll a[3][3];
}ans,shu,mp,bz,ans1,shu1;
ll n,a,b,c,p,x,y;
ll quick(ll a,ll b){
    a%=p;
    ll ans=1;
    while(b){
        if(b&1)ans=ans*a%p;
        a=a*a%p;
        b>>=1;
    }
    return ans;
}
arr matrix1(arr x,arr y){
    for(int i=0;i<2;i++)
        for(int j=0;j<2;j++){
            mp.a[i][j]=0;
            for(int k=0;k<2;k++)
                mp.a[i][j]=(mp.a[i][j]+x.a[i][k]*y.a[k][j]%(p-1))%(p-1);
        }
    return mp;
}
ll work2(ll k){
    ans1={1,0,0,0,1,0};
    while(k){
        if(k&1)ans1=matrix1(ans1,shu1);
        k>>=1;
        shu1=matrix1(shu1,shu1);
    }
    ll a1=(ans1.a[0][1]%(p-1));
    ll b1=(ans1.a[0][0]%(p-1));
    return (quick(x,a1)%p*quick(y,b1)%p*quick(quick(a,b),a1+b1-1)%p)%p;
}
int main(){
    //freopen("1.txt","r",stdin);
    //freopen("11.txt","w",stdout);
    p=1000000007;
    cin>>n>>x>>y>>a>>b;
    n--;
    if(x%p==0||y%p==0||a%p==0){cout<<0<<endl;;return 0;}
    x%=p,y%=p,a%=p;
    if(n==0)cout<<x%p<<endl;
    if(n==1)cout<<y%p<<endl;
    if(n==2)cout<<x*y%p*quick(a,b)%p<<endl;
    if(n>=3){
        shu1={1,1,0,1,0,0};
        ll sum=1;
        sum=(sum*work2(n-1))%p;
        cout<<sum<<endl;
    }
    return 0;
}

C-umi和弓道

題意:在起始點射箭,有n個靶子,現在你可以選擇給出一個與座標軸平行的擋板使得可以射中的靶子不超過k個。
題解:做出n條射線之後依次在座標軸上做出對應的投影,排個序往後取滑窗就行了。

#include "stdio.h"
#include "string.h"
#include "algorithm"
#include "iostream"
#include "math.h"
#include "vector"
#include "map"
using namespace std;
const int maxn=1e5+10;
const double INF=4e9+10;
int n,k,st,ed,num1,num2;
double dx[maxn],dy[maxn],ans=INF;
int main(){
    scanf("%lf%lf",&st,&ed);
    scanf("%d%d",&n,&k);
    for(int i=1;i<=n;i++){
        double x,y;
        scanf("%lf%lf",&x,&y);
        double l=1.0*(1.0*y-1.0*ed)/(1.0*x-1.0*st);
        if(y*ed<0)dx[++num1]=1.0*st-1.0*ed/l;
        if(x*st<0)dy[++num2]=1.0*ed-1.0*st*l;
    }
    k=n-k;
    sort(dx+1,dx+1+num1);
    sort(dy+1,dy+1+num2);
    for(int i=1;i+k-1<=num1;i++)ans=min(ans,dx[i+k-1]-dx[i]);
    for(int i=1;i+k-1<=num2;i++)ans=min(ans,dy[i+k-1]-dy[i]);
    if(ans==INF)puts("-1");
    else printf("%.8lf\n",ans);
    return 0;
}

F-maki和tree

題意:給出一個n個結點的樹,每個結點被標註了黑色或者白色,問簡單路徑上僅有一顆黑色結點的點對數量。
題解:符合題意的點對肯定只有兩種。第一種兩個端點都是白點且路徑上僅有一個黑點。第二種是其中一個端點爲黑點且路徑上沒有黑點。我們可以先將所有白點聯通塊處理出來,所以白點所代表的權值fifi就是白點聯通塊的點數量。枚舉黑點所連接的白點,假設有k個,第一種路徑的數量就是i=1kfifj\sum _{i=1}^kfi*fj,第二種就是i=1kfi\sum_{i=1}^kfi

#include "stdio.h"
#include "string.h"
#include "algorithm"
#include "iostream"
#include "math.h"
#include "vector"
#include "map"
using namespace std;
const int maxn=1e5+10;
const double INF=4e9+10;
typedef long long ll;
vector<int>G[maxn];
char s[maxn];
int n;
ll ans,f[maxn][2];
void dfs(int x,int fa){
    if(s[x-1]=='W')f[x][0]=1;
    for(int i=0;i<G[x].size();i++){
        int u=G[x][i];
        if(u==fa)continue;
        dfs(u,x);
        if(s[x-1]=='B')ans+=f[x][0]*f[u][0]+f[u][0];
        else ans+=f[x][1]*f[u][0]+f[x][0]*f[u][1];
        f[x][0]+=f[u][0];
        f[x][1]+=f[u][1];
    }
    if(s[x-1]=='B')f[x][1]=f[x][0]+1,f[x][0]=0;
}
int main(){
    scanf("%d",&n);
    scanf("%s",&s);
    for(int i=1;i<n;i++){
        int x,y;
        scanf("%d%d",&x,&y);
        G[x].push_back(y);
        G[y].push_back(x);
    }
    dfs(1,1);
    printf("%lld\n",ans);
    return 0;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章