【jzoj】2018.2.5NOIP普及组——C组模拟赛

本文解析了四道经典编程题目,包括动态规划求解公牛和母牛问题、利用杨辉三角原理解决最短路径问题、通过优化枚举提高求解最大公约数效率以及采用贪心算法应对数列转换挑战。

前言

今天第一次正式C组题,不过……比较恐怖。


正题


题目1:公牛和母牛(jzoj1292)

有n头牛,可以是公牛或母牛,每头公牛之间至少得有k头母牛。求方案数。

输入输出(建议跳过)

Input

第一行:两个空格隔开的整数N(N<=100000)和K。

Output

输出一个整数表示方法总数,答案可能很大,所以只需输出mod 5,000,011的值即可。

Sample Input

4 2

Sample Output

6

解题思路

推出动态转移方程,用f[i]来表示i头牛的方案数。

f[i]=f[i1]+1(i<=k+1)f[i]=f[i−1]+1(i<=k+1)

f[i]=f[ik]+f[i1](i>k+1)f[i]=f[i−k]+f[i−1](i>k+1)

因为如果在i处放置了公牛那么前k个就不能放,所以是f[i-k],放母牛的话就累加之前的方案数f[i-1]

代码

#include<cstdio>
using namespace std;
int n,k,f[100001];
int main()
{
    scanf("%d%d",&n,&k);
    k++;//加
    f[0]=1;//初始化
    for (int i=1;i<=n;i++)
    {
      if (i>k)
      {
        f[i]=(f[i-k]+f[i-1])%5000011;//动态转移
      }
      else
      {
        f[i]=(1+f[i-1])%5000011;//动态转移
      }
    }
    printf("%d",f[n]);//输出
}

题目2:最短路(jzοj3777)

每个点的权值遵守以下规律

f[1][1]=1
f[i][j]=f[i-1][j]+f[i][j-1]

然后求(1,1)到(m,n)的最小权值 mod 10000000007。

输入输出(建议跳过)

Input

一行两个正整数N,M,表示图的大小。

Output

一行一个整数Ans,表示答案模1000000007后的值。

Sample Input

1 2

Sample Output

6

解题思路

杨辉三角,也就是组合,然后用快速幂求组合公式

cnm=n!m!(nm)!cmn=n!m!(n−m)!

然后取膜的快速幂
cnm(modp)=((m!(nm)!)m!(p2)(modp)+n)(modp)cmn(modp)=((m!(n−m)!)∗m!(p−2)(modp)+n)(modp)

代码

#include<cstdio>
#include<algorithm>
#define mod 1000000007
using namespace std;
long long n,m,s,x,y;
long long qsm(long long x,long long c)//快速幂
{
    long long ans=1;
    while (c)
    {
        if (c&1) {ans=(ans*x)%mod;}
        x=(x*x)%mod;
        c>>=1;
    }
    return ans;
}
int main()
{
    scanf("%lld%lld",&n,&m);
    if (n<m) swap(n,m);
    x=1;y=1;
    for (long long i=n+2;i<=n+m+1;i++) x=(x*i)%mod;//求m!(n-m)!
    for (long long i=1;i<=m;i++) y=(y*i)%mod;//m!
    printf("%lld",(x*qsm(y,mod-2)%mod+n)%mod);//公式
}

题目3:Magical GCD(jzoj3780)

给出一个长n的序列,i到j的价值为i到j的最大公约数乘以它的长度。求最大价值

输入输出(建议无视)

Input

单个测试点包含多组数据。
输入的第一行是一个整数T表示数据组数。
每组数据的第一行是一个整数N,描述序列长度。
接下来N个数字,描述这个序列元素A[i]。

Output

对于每组测试数据输出一行,包含一个整数,表示序列最大的 Magical GCD。

Sample Input

1
5
30 60 20 20 20

Sample Output

80

解题思路

暴力枚举左到右会超时,所以我们用g[i]表示从i到r的gcd。
然后我们会发现这样会有许多重复的,只要我们过滤掉重复的gcd就好了。
所以我们先用一个右指针和左指针,发现重复时,就可以利用指针在下次枚举时无视自己

代码

#include<cstdio>
#include<iostream>
#define ll long long
using namespace std;
int ls[100001],nx[100001],t,n;
ll a[100001],g[100001],maxs;
ll gcd(ll x,ll y)//辗转相除法
{
  if (x%y==0) return y;
  else return gcd(y,x%y);
}
int main()
{
    scanf("%d",&t);
    for (int ti=1;ti<=t;ti++)
    {
        maxs=0;
        scanf("%d",&n);
        for (int i=1;i<=n;i++)
        {
            scanf("%lld",&a[i]);
            g[i]=a[i];
            nx[i]=i+1;//左指针
            ls[i]=i-1;//右指针
        }
        for (int r=1;r<=n;r++)
          for (int i=1;i<=r;i=nx[i])
          {
            g[i]=gcd(g[i],a[r]);//求最大公约
            maxs=max(g[i]*(r-i+1),maxs);//求最大值
            if (g[i]==g[i-1])//发现重复
            {
              nx[ls[i]]=nx[i];//修改指针让下次无视自己
              ls[nx[i]]=ls[i];//同上
            }
          }
        printf("%lld\n",maxs);
    }
}

题目4:Multiset(jzoj3781)

有一个数0,有以下两种操作

让一个数增加x=x+1
将一个数分裂为两个非负整数

给出一个序列,求出至少要多少次操作才可以变为这个序列

输入输出(建议无视)

Input

第一行为一个整数N,描述最终集合的大小。
第二行为N个非负整数,为最终集合的每一个元素。

Output

输出唯一一行,Alice 最少玩的轮数。

Sample Input

Sample Input 1
1
0

Sample Input 2
4
1 1 1 1

Sample Input 3
5
0 3 0 3 0

Sample Output

Sample Output 1
0
Sample Output 2
3
Sample Output 3
5

解题思路

首先感谢纪中dalao的帮助
反推,就变成了减去1和合并这两种操作。
然后贪心:用桶w[i]来表示在第i步有多少个数会变为0

代码

#include<cstdio>
#include<iostream>
using namespace std;
int n,x,maxs,s;
int w[1000001];
int main()
{
    scanf("%d",&n);
    for (int i=1;i<=n;i++)
    {
        scanf("%d",&x);
        w[x]++;//桶
        maxs=max(maxs,x);//记录最大值
    }
    s=w[0];//0的个数
    for (int i=1;i<=maxs;i++)
    {
        s=(s+1)/2+w[i];//合并0并加入新的0
    }
    while (s>1) {s=(s+1)/2;maxs++;}//合并剩余部分
    printf("%d",maxs);//输出
}

转载于:https://www.cnblogs.com/sslwyc/p/9218591.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值