牛客寒假算法基础集训营(全6次)

目录

 

目录

牛客寒假算法基础集训营4

E、Applese 涂颜色

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

C、小a与星际探索

D、小a与黄金街道

牛客寒假算法基础集训营5

J、迷宫

G、酷炫数字

牛客寒假算法基础集训营6 

B、煤气灶

D、美食 

G、区间或和

E、海啸 

牛客寒假算法基础集训营3

D、处女座的训练

I、处女座的约会

牛客寒假算法基础集训营2

G、处女座与复读机

h、处女座的测验1


 

牛客寒假算法基础集训营4

E、Applese 涂颜色

theme:求2^n%(10^9+7),其中n可到10^100000;

solution:如果用python3做的话,直接用pow()函数。一般做法:肯定要用快速幂,但n太大还是会超时,且不好存。利用费马小定理转化为求2^(n%(10^9+7-1))%(10^9+7)。即将幂次由n转换为n%(10^9+7-1).

print(pow(2, int(input().split()[0]), 10**9 + 7))

 

//OK???????
#include
#include
#include
#include
#include
#include
using namespace std;
#define far(i,n) for(int i=0;i=0;--i)
typedef long long ll;
#define j2h(x) (3.1415926*(x)/180.0)
const int mod=1e9+7;

ll quickPow(int n)
{
    ll ans=1;
    ll a=2;
    while(n)
    {
        if(n&1)ans=(ans*a)%mod;
        a=(a*a)%mod;
        n/=2;
    }
    return ans;
}

int main()
{
    string n,m;
    cin>>n>>m;
    ll k=n[0]-'0';
    int l=n.length();
    //cout<

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

C、小a与星际探索

theme:n个星球。小a驾着飞船要从星球1到n,每个星球都有一个能量数pi,能从星球i到j,当且仅当pi>pj,小a 的飞船有个耐久值t,没到一个星球耐久值变为t=t^pi,求小a到达n号星球的最大耐久值,若不能到达或达到后耐久值为0,则输出-1.1<=n,pi<=3000

solution:背包问题。首先达到下一个星球有限制,所以先从大到小排序(1号与n号必须到达所以不排),先判断能否到达n号。

用dp[j]表示是否可以达到j的耐久值,初始时dp[a[0]*a[n-1]]=1;则从大到小遍历时若dp[j]=1,则dp[j^a[i]]=1(到达该星球)。

最后的结果找到最大的j使得dp[j]=1即可。(考虑时可想用dp[i][j]表示前i个星球是否可以得到j的耐久值,则转移时考虑是否达到第i个星球)

#include
#include
#include
#include
#include
#include
using namespace std;
#define far(i,n) for(int i=0;i=0;--i)
typedef long long ll;

int a[3005];
int dp[4100]={0};

int main()
{
    int n;
    cin>>n;
    far(i,n)
        scanf("%d",&a[i]);
    sort(a+1,a+n-1);
    if(a[0]<=a[n-1])
    {
        cout<<-1;
        return 0;
    }

    dp[a[0]^a[n-1]]=1;
    for(int i=n-2;i>=1;--i){
        if(a[i]<=a[n-1])break;
        if(a[i]>=a[0])continue;
        for(int j=0;j<=4095;++j)
            if(dp[j])
                dp[j^a[i]]=1;
    }
    for(int j=4095;j>=1;--j)
    {
        if(dp[j])
        {
            cout<

D、小a与黄金街道

theme: 一条道路n米,左端点为0,右端点为n,a,b两个人速度都为1m/s,a从1出发走到n-1,b从n-1出发走到1,开始时他们各自拥有A与B的黄金数,若某一时刻他们分别走到点x,y,满足gcd(n,x)=1,gcd(n,y)=1,则这是他们的黄金数分别变为A=A*k^x,B=B*k^y,k已知,a走到n-1时结束,求最终A+B。结果mod 10^9+7

solution:首先根据"若gcd(n,x)=1,则gcd(n,n-x)=1"这个定理(反证法证),与a,b速度相同,x+y=n可知a可获得黄金的点是与n互质的点,b也一样,所以先通过欧拉函数求出小于n且与n互质的数的个数h(n),可知这n个数的和为h(n)*n/2,a,b贡献相同可把指数提出,最终答案为(A+B)*k^(h(n)*n/2),再用快速幂算。还可再用优化。

#include
#include
#include
#include
#include
#include
using namespace std;
#define far(i,n) for(int i=0;i=0;--i)
typedef long long ll;

int mod=1e9+7;

ll eular(ll n)
{
    ll ans=n;
    for(int i=2;i*i<=n;++i)
    {
        if(n%i==0)
        {
            ans-=ans/i;
            while(n%i==0)
                n/=i;
        }
    }
    if(n>1)
        ans-=ans/n;
    return ans;
}

ll qpow(ll a,ll b)
{
    ll ans=1;
    while(b)
    {
        if(b%2)ans=ans*a%mod;
        a=a*a%mod;
        b>>=1LL;
    }
    return ans;
}

int main()
{
    int n;
    ll k,a,b;
    cin>>n>>k>>a>>b;
    a%=mod,b%=mod,k%=mod;
    ll e=eular(n)*n/2;
    e%=mod-1; //也可不加
    ll ans=(a+b)%mod*qpow(k,e)%mod;
    cout<

牛客寒假算法基础集训营5

J、迷宫

theme:给定n*m的迷宫,其中*表示障碍物,.表示可走的,给定初始位置r,c(保证不是障碍物),问迷宫中共有多少位置是可达的,且最多只能往左走x步,往右走y步。

solution:bfs可以求得那些位置是可达的,再加两个变量fl,fr记录到达这里时已经往左往右走了多少步,作为限制条件

#include
#include
#include
#include
#include
#include
using namespace std;
#define far(i,n) for(int i=0;i=0;--i)
typedef long long ll;

int n,m,r,c;
ll x,y;
char a[1005][1005];
int X[4]={0,0,-1,1};
int Y[4]={-1,1,0,0};//l,r,u,d

struct _a
{
    int i,j,fl,fr;
    _a(int a,int b,int c,int d)
    {
        i=a;
        j=b;
        fl=c;
        fr=d;
    }
};

int bfs()
{
    queue<_a>q;
    q.push(_a(r,c,0,0));
    int ans=0;
    a[r][c]='*';

    while(!q.empty())
    {
        int i=q.front().i,j=q.front().j,fl=q.front().fl,fr=q.front().fr;
//cout<=n||dy>=m||a[dx][dy]=='*')
                continue;
            if(k==0)
                ++nl;
            else if(k==1)
                ++nr;
            a[dx][dy]='*';
            if(nl<=x&&nr<=y)
                q.push(_a(dx,dy,nl,nr));
        }
//cout<>n>>m>>r>>c>>x>>y;
    --r,--c;
    far(i,n)
    {
        getchar();
        scanf("%s",a[i]);
    }
    int ans=bfs();
    cout<

G、酷炫数字

theme:T组询问,每次询问给定n,求因子个数为n的最小数。1<=T,n<=1000000

solution:t很大,为不超时,最好先算出n为1~1000000的答案,这样T次询问直接输出即可。

先计算1~1000000之间数的因子数:类似线性筛法,每个数一定是它的倍数的因子,所以用a[i]表示i的因子个数,从1~1000000循环j,则a[j的倍数]++,而题目是已知因子个数求i,所以我们用另一个数组b[i]表示将a[i]的值与索引颠倒即为结果,而b[i]必然<=i,所以a[i]中没有的值即在i>1000000里,输出-1.

#include
#include
#include
#include
#include
#include
using namespace std;
#define far(i,n) for(int i=0;i=0;--i)
typedef long long ll;

int a[1000010],b[1000010];

int main()
{
    int t;
    cin>>t;
    memset(a,0,sizeof(a));
    memset(b,-1,sizeof(b));
    for(int i=1;i<=1000000;++i)
        for(int j=i;j<=1000000;j+=i)
            a[j]++;
//    for(int j=1;j<=20;j++)
//        cout<i)
            b[a[i]]=i;
    while(t--)
    {
        int n;
        scanf("%d",&n);
        printf("%d\n",b[n]);
    }
}

牛客寒假算法基础集训营6 

B、煤气灶

theme:第一天钱数为n,之后每一天都会获得比前一天多d的钱。已知煤气灶要m元,问最少哪天可以买了?(给定x,结果<=x<=10^9)

solution:某一天的钱总数即是等差数列求和:s=a0*x+x*(x-1)*d/2,解法即循环求s直到>=m,但由于数据大的话可能到x到10^9超时,所以用二分法找到第一个>=m的(即lower_bound()但直接用函数得先定义数组计算,耗时耗空间,所以手写该函数)

#include
#include
#include
#include
#include
#include
using namespace std;
#define far(i,n) for(int i=0;i=0;--i)
typedef long long ll;

int main()
{
    double n,m,d,midm;
    ll x,i=1,mid;
    cin>>n>>m>>d>>x;
    while(i=m)
            x=mid;
        else
            i=mid+1;
    }
    cout<

D、美食 

theme:n中美食按顺序摆成一排,第i中美食可吃a[i]口,一次要吃两口,这两口要么是同一种美食,要么是相邻的,问最多能吃几次?

solution:贪心。从左往右遍历,如果前一种美食还有剩的(该策略下为1),则优先与前一种搭配,然后自我搭配为一次直至剩1或0

#include
#include
#include
#include
#include
#include
using namespace std;
#define far(i,n) for(int i=0;i=0;--i)
typedef long long ll;

ll a[100010]={0};

int main()
{
    int n;
    cin>>n;
    far(i,n)
        scanf("%lld",&a[i]);
    ll ans=a[0]/2;
    a[0]%=2;

    for(int i=1;i

 G、区间或和

theme:给定a、b求 a|(a+1)|(a+2)|...|(b-1)|b。0≤a,b≤10^18,a≤b

solution:位运算当然转化为二进制考虑每一位。从低位开始,每一位上都是每过2^i(从0开始)变为0或1

#include
#include
#include
#include
#include
#include
using namespace std;
#define far(i,n) for(int i=0;i=0;--i)
typedef long long ll;

ll len[100];
int ans[100]={0};

int main()
{
    ll x=2;
    far(i,60)
    {
        len[i]=x;
        x*=2;
    }
    ll a,b;
    while(~scanf("%lld%lld",&a,&b))
    {
        ll l=b-a+1;
        far(i,60)
        {
            if(l>(len[i]/2))
            {
                ans[i]=1;
                continue;
            }
            if(a%len[i]>=0&&b%len[i]<=len[i]/2-1&&a%len[i]<=b%len[i])
                ans[i]=0;
            else
                ans[i]=1;
        }
        ll res=0;
        for(int i=59;i>=0;--i)
            res=res*2+ans[i];
        printf("%lld\n",res);
    }
}

E、海啸 

theme:给定n*m矩阵表示n*m座楼的高度,给定水深d,高度低于d的楼会被淹没。有q次询问,每次询问给定a,b,x,y询问以第a行第b座楼和第x行第y座楼为对角的矩形区域内,不会被淹没的楼数。

solution:即是计算出矩形区域内高度>=d的楼个数。由于询问有多组,所以采用动态规划一次性算出即可在询问时避免重复计算了。递归式:ans[i][j]=ans[i-1][j]+ans[i][j-1]-ans[i-1][j-1]。ans[i][j]表示以[0][0]和[i][j]为对角的矩形区域。

#include
#include
#include
#include
#include
#include
using namespace std;
#define far(i,n) for(int i=0;i=0;--i)
typedef long long ll;


int main()
{
    int n,m;
    ll d;
    cin>>n>>m>>d;
    ll h[n+5][m+5];
    int live[n+5][m+5];
    ll ans[n+5][m+5];
    far(i,n)
        far(j,m)
            scanf("%lld",&h[i][j]);

    far(i,n)
        far(j,m)
            if(h[i][j]>=d)
                live[i][j]=1;
            else
                live[i][j]=0;
//dp求以[0][0]和[i][j]为对角的矩形区域内不被淹没的楼个数
    far(i,n)
        far(j,m)
        {
            ans[i][j]=0;
            if(j-1>=0)
                ans[i][j]+=ans[i][j-1];
            if(i-1>=0)
                ans[i][j]+=ans[i-1][j];
            if(j-1>=0&&i-1>=0)
                ans[i][j]-=ans[i-1][j-1];
            ans[i][j]+=live[i][j];
        }
//    far(i,n){
//        far(j,m)
//            cout<>q;
        far(i,q)
        {
            int a,b,x,y;
            scanf("%d%d%d%d",&a,&b,&x,&y);
            int res=ans[x-1][y-1];
            if(b-2>=0)
                res-=ans[x-1][b-2];
            if(a-2>=0)
                res-=ans[a-2][y-1];
            if(a-2>=0&&b-2>=0)
                res+=ans[a-2][b-2];
            printf("%d\n",res);
        }

}

牛客寒假算法基础集训营3

D、处女座的训练

theme:要做n个题目,每个题目有一个刷掉本题要花费的时间ai和本题每分钟会带来的疲倦值bi。在做题时,未做的题目每分钟会产生疲惫值,正在做的这题不会产生疲惫值,问昨晚这些题目最少会产生多少疲惫值

solution:选定刷题顺序后,总疲惫值计算方法为遍历除最后一题的所有题,总疲惫+=当前做题的解题时间*剩下所有未解题带来的疲惫值之和。由这个式子可知ai小的、bi大的题应先解,这样总乘积和就会小,由贪心知bi/ai小的题先解

注:由浮点数除法不准确性,比较时转化为乘: return b*c.a>a*c.b;

//ok
#include
#include
#include
#include
#include
#include
using namespace std;
#define far(i,n) for(int i=0;i=0;--i)
typedef long long ll;

struct _a
{
    int a,b;
    bool operator<(_a c)const
    {
       // float x=1.0*b/a,y=1.0*c.b/c.a;
        return b*c.a>a*c.b;
    }
}a[100010];

int main()
{
    int n;
    cin>>n;
    ll sum=0;
    far(i,n){
        scanf("%d%d",&a[i].a,&a[i].b);
        sum+=a[i].b;
    }
    sort(a,a+n);
//    far(i,n)
//        cout<

I、处女座的约会

theme:一条龙有n个头,用01序列表示,1表示邪恶的头,0表示善良的头,你每次可以砍掉最右边的头而在最左边补一个头。如果你砍的是1,则你可以自主选择补0还是1;而如果你砍掉的是0,则由该龙选择补0还是1,龙总会跟你作对。

solution:先注意题目有一点误导性,101时也是可最终变为000的:砍掉最右边1,左边补1,变为110,再砍掉0后无论是011还是111都必能变为000。明白了这个就可以看出,处女座可通过每次砍了1后补0来把左边逐渐都变成1,使得达到无0、1相间的局面,即到达111000或00111这样的。所以必赢。

#include
#include
#include
#include
#include
#include
using namespace std;
#define far(i,n) for(int i=0;i=0;--i)
typedef long long ll;

int main()
{
    int t;
    cin>>t;
    while(t--)
    {
        printf("cnznb\n");
    }
}

G、处女座和小姐姐3

theme:给定l,r表示整数范围[l,r],0<=l<=r<=10^18,问[l,r]间的整数中,数字中含有6的整数有几个

solution:考虑计算[0,r]与[0,l)之间的再相减。思路是按个位是6、十位是6等依次分开考虑。对于个位是6,考虑r/10,因为每10个数出现一次个位为6,所以为r/10个,再判断r%10与6的大小判断还有没有一个;而十位是6则除以100,每100有9个(6~:~为1到9除6以外);依次类推,直到除后为0。

 

牛客寒假算法基础集训营2

A、处女座的签到题

theme:给定n个点的坐标,求由其中点组成的三角形中面积第k大的面积。

solution:遍历得到三个点,由二维叉积(向量积)算三角形的面积。由于求第k大,而不满足构成三角形条件的两个向量的叉积为0,不影响结构,所以可以不用单独判断能否构成三角形。最后求第k大时,可排序,也可用nth_element(a,a+k-1,a+n),会将第k小的数放在它从小到大排应该放的位置(其它数不一定)。注意这题是求“大”,所以转换一下为n-k.

#include
#include
#include
#include
#include
#include
#include
using namespace std;
#define far(i,n) for(int i=0;i=0;--i)
typedef long long ll;

ll X[105];
ll Y[105];
ll ans[1000005];

int main()
{
    int t;
    cin>>t;
    while(t--)
    {
        int n,k;
        scanf("%d%d",&n,&k);
        far(i,n)
            scanf("%lld%lld",&X[i],&Y[i]);
        int cnt=0;
        far(i,n-2)
            for(int j=i+1;j

G、处女座与复读机

theme:给定一个字符串s,问经过以下操作两次后能否得到字符串t。(全为小写字母)每次可以:

(1)将其中一个字母改为另一个字母。(2)插入一个字母。(3)删除一个字母

solution:DP:用dp[i][j]表示操作从s[1..i]到t[1...j]至少需要的操作数,则若s[i]==t[j],则dp[i][j]=dp[i-1][j-1],否则为min(dp[i-1][j]+1,dp[i][j-1]+1,dp[i-1][j-1]+1)

#include
#include
#include
#include
#include
#include
#include
using namespace std;
#define far(i,n) for(int i=0;i=0;--i)
typedef long long ll;

int dp[110][110];

int main()
{
    char s[200],t[200];
    scanf("%s%s",s,t);
    int l1=strlen(s),l2=strlen(t);
    far(i,l1)dp[i][0]=i;
    far(i,l2)dp[0][i]=i;
    for(int i=1;i<=l1;++i)
        for(int j=1;j<=l2;++j)
        {
            if(s[i]==t[j])
                dp[i][j]=dp[i-1][j-1];
            else
                dp[i][j]=min(dp[i-1][j-1]+1,min(dp[i-1][j]+1,dp[i][j-1]+1));
        }

    if(dp[l1][l2]<=2)
        cout<<"YES\n";
    else
        cout<<"NO\n";
}

h、处女座的测验1

theme:输出2000个数(1-4*10^8之间),要求:1、这2000个数两两互质;2、任意两数的乘积的因数>10

solution:既然两两互质,则它们乘积的因子即为这两个数的因子互乘,所以要求任意一个数的因数>=4,乘积的因数就>=16了,谈到互质应该联想到质数,质数只有2个因子,所以两个质数的乘积的因数为4正好满足,所以找出前4000个质数两个想乘凑2000个,考虑到范围,最好首尾相乘

//ok
#include
#include
#include
#include
#include
#include
using namespace std;
#define far(i,n) for(int i=0;i=0;--i)
typedef long long ll;

int p[5000];
int pcnt=0;
bool Iscomp[40000]={false};

//线性筛法
void sieve2()
{
    for(int i=2;i<40000;i++)
    {
        if(!Iscomp[i])p[pcnt++]=i;
        for(int j=0;j

 

你可能感兴趣的:(acm,dp,快速幂,欧拉函数,费马小定理)