2020牛客寒假算法基础集训营1 题解

目录

【A-honoka和格点三角形】

【B-kotori和bangdream】

【C-umi和弓道】

【D-hanayo和米饭】

【E-rin和快速迭代】

【F-maki和tree】

【G-eli和字符串】

【H-nozomi和字符串】

【I-nico和niconiconi】

【J-u's的影响力】


 

【A-honoka和格点三角形】

呕,花我时间最多的一道题,因为一开始推导错了各种思维发散,后来节奏全乱了。

因为面积为1的三角形只有两种情况,一种是底边为1,高为2;一种是底边为2,高为1。因此我们可以从这里入手,考虑底边分别为1和2时的情况数*边数,特别注意的是,存在同时底边为1和底边为2的三角形,所以要删去重复部分。

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=1e6+5;
const ll mod=1e9+7;
int main()
{
    ll n,m;
    scanf("%lld%lld",&n,&m);
    ll a=(((n-2)*n%mod)*(2*m-2)%mod+((m-2)*m%mod)*(2*n-2)%mod)%mod; //底边为2
    ll b=(((n-2)*(n-1)%mod)*(2*m-4)%mod+((m-2)*(m-1)%mod)*(2*n-4)%mod)%mod;
    printf("%lld\n",(a+b)%mod);
    return 0;
}

【B-kotori和bangdream】

签到题一。

printf("%.2f\n",(double)n*((x/100.0)*a+(1-x/100.0)*b));

【C-umi和弓道】

将每个点与起始点(x0,y0)连接并记录下与X轴或Y轴的交点(如果有),分别在X轴和Y轴用双指针法确定包含n-k个点的最短长度,最后输出最小值(不可能输出-1)。

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=1e6+5;
vector <double> v[3];
int main()
{
    double x,y; scanf("%lf%lf",&x,&y);
    int n,k; scanf("%d%d",&n,&k);
    for(int i=0;i<n;i++){
        double xx,yy; scanf("%lf%lf",&xx,&yy);
        double k=1.0*(y-yy)/(x-xx);
        double b=y-k*x;
        if(x*xx<0) v[0].push_back(y-x*(y-yy)/(x-xx));
        if(y*yy<0) v[1].push_back(x-y*(x-xx)/(y-yy));
    }
    double ans=1e18;
    for(int t=0;t<2;t++){
        int l=v[t].size();
        if(l>=n-k){
            sort(v[t].begin(),v[t].end());
            for(int i=n-k-1,j=0;i<l;i++,j++)
                ans=min(ans,v[t][i]-v[t][j]);
        }
    }
    if(ans==1e18) printf("-1\n");
    else printf("%.8f\n",ans);
    return 0;
}

【D-hanayo和米饭】

签到题二,排序遍历查找即可。

【E-rin和快速迭代】

签到题三,暴力迭代计数即可。

【F-maki和tree】

首先,我们可以通过在纸上画画算算得到解法:即以黑色的节点为根结点并且只连接白节点构造出一棵树,在构造的过程中,假如我们定义Ans记录答案,那么Ans=Ans+已构造的树上的所有节点数目C*新加进去的子树的节点数目Cou,直到构造完成。而且我们比较容易得到,把所有黑色结点单独拿出构造这样一棵树,那么总和Ans就是我们要的答案。

但是,在这个过程中,白节点子树的所有节点数目是我们重复计算的,因此我们可以预先处理出每个白节点子树的所有节点数目。用队列处理一下并且记录一下父亲结点就可以得到。

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=1e6+5;
char s[N];
vector <int> vec[N];
int pre[N];
int cnt[N];
int vis[N];
ll cul(int r)
{
    ll ret=0;
    int l=vec[r].size();
    ll t=1;
    for(int i=0;i<l;i++){
        int p=vec[r][i];
        int cou=cnt[pre[p]];
        ret+=t*cou;
        t+=cou;
    }
    return ret;
}
int main()
{
    int n; scanf("%d",&n);
    scanf("%s",s+1);
    for(int i=1;i<n;i++){
        int x,y; scanf("%d%d",&x,&y);
        if(s[x]!='B') vec[y].push_back(x);
        if(s[y]!='B') vec[x].push_back(y);
    }
    for(int i=1;i<=n;i++) pre[i]=i;
    for(int i=1;i<=n;i++){
        if(s[i]=='B'||vis[i]) continue;
        queue <int> q;
        while(!q.empty()) q.pop();
        int cou=1;
        vis[i]=1;
        q.push(i);
        while(!q.empty()){
            int pos=q.front();q.pop();
            int l2=vec[pos].size();
            for(int j=0;j<l2;j++){
                int pp=vec[pos][j];
                if(vis[pp]) continue;
                vis[pp]=1;
                q.push(pp);
                pre[pp]=i;
                cou++;
            }
        }
        cnt[i]=cou;
    }
    /*
    for(int i=1;i<=n;i++)
        printf("pre[%d]=%d\n",i,pre[i]);
    for(int i=1;i<=n;i++)
        if(pre[i]==i) printf("cnt[%d]=%d\n",i,cnt[i]);
    */
    ll ans=0;
    for(int i=1;i<=n;i++){
        if(s[i]=='B'){
            ans=ans+cul(i);
        }
    }
    printf("%lld\n",ans);
    return 0;
}
/*
Input
7
BWWWWWB
1 5
1 6
1 2
2 3
3 4
3 7
Output
15
*/

【G-eli和字符串】

字符串仅由小写字母组成,那么如果存在答案最多只有26种可能,所以我们直接判断每一种可能字母的k个相同该字母的最短长度并更新答案即可。解法:首先我们跑出每个位置pos的下一个字母 'a'+i 的位置p[i][pos],便于后续位置的转移。对于字母C,我们首先找出第一个满足k个相同字母C的子串,然后依次将头尾往后移动到下一个相同字母C的位置,并更新最短长度。以此类推。

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=1e6+5;
int a[30];
char s[N];
int p[30][N];
int main()
{
    int n,k; scanf("%d%d",&n,&k);
    scanf("%s",s+1);
    int l=strlen(s+1),ma=0;
    for(int i=1;i<=l;i++)
        a[s[i]-'a']++,ma=max(ma,a[s[i]-'a']);
    if(ma<k) printf("-1\n");
    else{
        int ans=l;
        for(int i=0;i<26;i++) p[i][l]=-1;
        for(int j=l;j>0;j--){ //这个位置的下一个位置
            for(int i=0;i<26;i++){
                if(s[j]-'a'==i) p[i][j-1]=j;
                else p[i][j-1]=p[i][j];
            }
        }
        for(int i=0;i<26;i++){
            if(a[i]<k) continue;
            int cnt=1,mi=l;
            int bg=p[i][0],ed=bg;
            while(cnt<k) ed=p[i][ed],cnt++;
            mi=min(mi,ed-bg+1);
            while(p[i][ed]!=-1){
                ed=p[i][ed];
                bg=p[i][bg];
                mi=min(mi,ed-bg+1);
            }
            ans=min(ans,mi);
        }
        printf("%d\n",ans);
    }
    return 0;
}

【H-nozomi和字符串】

做法与上一题类似,细节部分稍有不同。

两种情况,连续全为1或0。跑出每个位置的下一个0/1的位置,便于后续操作中位置的转移。

以全为1为例:我们定义bg,ed分别记录子串的起点和终点,我们先跑出第一个用k次转换的连续1串。然后依次取消头部的转换并且将尾部的下一个0转换成1,在这个过程中更新最长连续串的长度。细节见代码,应该比较好懂。

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=1e6+5;
char s[N];
int p[3][N];
int main()
{
    int n,k; scanf("%d%d",&n,&k);
    scanf("%s",s+1);
    int l=n;
    p[0][l]=p[1][l]=-1;
    for(int j=l;j>0;j--){ //这个位置的下一个位置
        for(int i=0;i<2;i++){
            if(s[j]-'0'==i) p[i][j-1]=j;
            else p[i][j-1]=p[i][j];
        }
    }
    int ans=0;
    //全为0/1
    for(int i=0;i<2;i++){
        int cnt=1;
        int bg=1,ed=p[i][0];
        while(cnt<k&&p[i][ed]!=-1) ed=p[i][ed],cnt++;
        if(p[i][ed]==-1) ed=l;
        else ed=p[i][ed]-1;
        ans=max(ans,ed-bg+1);
        while(p[i][ed]!=-1){
            ed=p[i][ed];
            if(p[i][ed]==-1) ed=l;
            else ed=p[i][ed]-1;
            if(s[bg]-'0'==i) bg++;
            else bg=p[i][bg]+1;
            ans=max(ans,ed-bg+1);
        }
        ans=max(ans,l-bg+1);
    }
    printf("%d\n",ans);
    return 0;
}

【I-nico和niconiconi】

动态规划,暴力枚举所有可能性选择最优。

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=1e6+5;
string s;
ll dp[N];
int main()
{
    int n; ll a,b,c;
    cin>>n>>a>>b>>c>>s;
    s="1"+s;
    for(int i=1;i<=n;i++){
        dp[i]=dp[i-1];
        if(i>=3&&s.substr(i-3,4)=="nico")
            dp[i]=max(dp[i],dp[i-4]+a);
        if(i>=5&&s.substr(i-5,6)=="niconi")
            dp[i]=max(dp[i],dp[i-6]+b);
        if(i>=9&&s.substr(i-9,10)=="niconiconi")
            dp[i]=max(dp[i],dp[i-10]+c);
    }
    printf("%lld\n",dp[n]);
    return 0;
}

【J-u's的影响力】

(不补)

官方题解:

#include<bits/stdc++.h>
using namespace std;
#define ll long long
struct mt{
    ll a[3][3];
};
mt t(mt a,mt b,ll mod){
    mt res;
    int i,j,k;
    for(i=0;i<3;i++){
        for(j=0;j<3;j++){
            res.a[i][j]=0;
            for(k=0;k<3;k++){
                res.a[i][j]+=a.a[i][k]*b.a[k][j]%mod;
                res.a[i][j]%=mod;
            }
        }
    }
    return res;
}
mt power(mt a,ll b,ll mod){
    mt res;
    int i,j;
    for(i=0;i<3;i++){
        for(j=0;j<3;j++){
            res.a[i][j]=0;
        }
    }
    res.a[0][0]=res.a[1][1]=res.a[2][2]=1;
    while(b){
        if(b&1)res=t(res,a,mod);
        b>>=1;
        a=t(a,a,mod);
    }
    return res;
}
ll feb(ll n,ll mod){
    mt temp;
    int i,j;
    for(i=0;i<3;i++){
        for(j=0;j<3;j++){
            temp.a[i][j]=0;
        }
    }
    temp.a[0][1]=temp.a[1][1]=temp.a[1][0]=temp.a[1][2]=1;
    mt res=power(temp,n-1,mod);
    return (res.a[0][0]+res.a[0][1])%mod;
}
ll feb2(ll n,ll mod){
    mt temp;
    int i,j;
    for(i=0;i<3;i++){
        for(j=0;j<3;j++){
            temp.a[i][j]=0;
        }
    }
    temp.a[0][1]=temp.a[1][1]=temp.a[1][0]=temp.a[1][2]=temp.a[2][2]=1;
    mt res=power(temp,n-1,mod);
    return (res.a[0][0]+2*res.a[0][1]+res.a[0][2])%mod;
}
ll power(ll a,ll b,ll mod){
    ll res=1;
    while(b){
        if(b&1)res=res*a%mod;
        b>>=1;
        a=a*a%mod;
    }
    return res;
}
int main()
{
    int m=1e9+7;
    int i,j;
    ll n,x,y,a,b;
    cin>>n>>x>>y>>a>>b;
    if(n==1){cout<<x%m;return 0;}
    if(n==2){cout<<y%m;return 0;}
    if(x%m==0||y%m==0||a%m==0){cout<<0;return 0;}
    x%=m;
    y%=m;
    a=power(a%m,b,m);       //这里要注意a对m取模
    cout<<power(x,feb(n-2,m-1),m)*power(y,feb(n-1,m-1),m)%m*power(a%m,feb2(n-2,m-1)%(m-1),m)%m<<endl;
}

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值