2016 Multi-University Training Contest 6 解题报告

1. 1001-HDU 5793 A Boring Question

题意:给定n,m,求的值。其中


题解:

二项式定理:


特别的,


即,


其中


所以:


用快速幂来求解m^(n+1)由于要取模,但是有除法所以用费马小定理可得:a / c mod p== a * c^(p-2) mod p。

代码:

#include <iostream>
#include <algorithm>
#include <cstdio>
using namespace std;
const int MOD=1000000007;
typedef long long LL;

LL Pow(LL a,LL n)
{
    LL ans=1;
    while(n){
        if(n&1)ans=ans*a%MOD;
        n>>=1;
        a=a*a%MOD;
    }
    return ans;
}
int main()
{
    int T;
    scanf("%d",&T);
    while(T--)
    {
        LL n,m;
        scanf(" %I64d %I64d",&n,&m);
        printf("%I64d\n",(Pow(m,n+1)-1)*Pow(m-1,MOD-2)%MOD);
    }
    return 0;
}

2. 1002- HDU 5794 A Simple Chess

题意:一个n*m的棋盘,一个棋子想从(1,1)走到(n,n),棋子每步可从(x1,y1)到达(x2,y2),

当且仅当

另外,棋盘上有r处障碍,棋子无法到达障碍处,问棋子从(1,1)到达(n,m)有多少种走法?

题解:


根据题意,棋子只能走(x+1,y+2)①或者(x+2,y+1)②这两个方向 ,所以只有满足一定条件的位置才有可能到达。我们假设①方向走了a步,②方向走了b步,那么我们可以得到满足条件的位置一定是x=a+2b,y=2a+b,所以可以得到只有(x+y)是3的整数倍的位置才有可能到达。并且(1/2<=y/x<=2)

我们先不考虑障碍物,则到达每个点的方案数构成一个杨辉三角,即每个点的方案数等于它两肩上的数之和。我们可以用坐标计算出这个点在杨辉三角中的 位置。杨辉三角第x行第y列(x,y下标均从0开始)的数为组合数C(x,y),大数组合数用Lucas定理。

然后考虑障碍物,到达终点的方案数等于(不考虑障碍物的方数)-(起点到障碍物的方案数)*(障碍物到终点的方案数),而(起点到障碍物的方案数)的计算也要减去其他障碍物对它的影响

代码 :

#include <iostream>
#include <cstdio>
#include <algorithm>
using namespace std;
const int MAX=100+5;
const int MOD=110119;
typedef long long LL;
struct node
{
    LL x,y,val;
    node(LL _x=0,LL _y=0,LL _val=0):x(_x),y(_y),val(_val) {};
    bool operator <(const node &a) const
    {
        return this->x<a.x||(this->x==a.x&&this->y<a.y);
    }
} ob[MAX];

LL f[200000];
void init(int p)
{
    f[0] = 1;
    for(int i = 1; i <= p; ++i)
        f[i]  = f[i-1] * i % p;
}
LL pow_mod(LL a, LL x, int p)
{
    LL ret = 1;
    a %= p;
    while(x)
    {
        if(x & 1)
        {
            ret = ret * a % p;
            --x;
        }
        else
        {
            a = a * a % p;
            x >>= 1;
        }
    }
    return ret;
}
LL Lucas(LL n, LL k, int p)
{
    LL ret = 1;
    while(n && k)
    {
        LL nn = n % p, kk = k % p;
        if(nn < kk) return 0;
        ret = ret * f[nn] * pow_mod(f[kk] * f[nn - kk] % p, p - 2, p) % p;
        n /= p;
        k /= p;
    }
    return ret;
}
LL waycnt(LL x1,LL y1,LL x2,LL y2,int p)
{
    LL x=x2-x1;
    LL y=y2-y1;
    if((x1+y1)%3!=2) return 0;
    if((x2+y2)%3!=2) return 0;
    if((x+y)%3!=0) return 0;
    LL row=(x+y)/3;
    LL col=x-row;
    if(col<0||col>row) return 0;
    return Lucas(row,col,p);
}
int main()
{
    init(MOD);
    int tcase=1;
    LL n,m,r;
    while(cin>>n>>m>>r)
    {
        for(int i=0; i<r; i++)
        {
            cin>>ob[i].x>>ob[i].y;
        }
        sort(ob,ob+r);
        for(int i=0; i<r; i++)
        {
            ob[i].val=waycnt(1,1,ob[i].x,ob[i].y,MOD);
            for(int j=0; j<r; j++)
            {
                if(ob[j].x<ob[i].x&&ob[j].y<ob[i].y)
                    ob[i].val=((ob[i].val-(ob[j].val*waycnt(ob[j].x,ob[j].y,ob[i].x,ob[i].y,MOD)%MOD))%MOD+MOD)%MOD;
            }
        }
        LL ans=waycnt(1,1,n,m,MOD);
        for(int i=0; i<r; i++)
        {
            if(ob[i].x==n&&ob[i].y==m)
            {
                ans=0;
                break;
            }
            if(ob[i].x<n&&ob[i].y<m)
                ans=((ans-(ob[i].val*waycnt(ob[i].x,ob[i].y,n,m,MOD)%MOD))%MOD+MOD)%MOD;
        }
        printf("Case #%d: %I64d\n",tcase++,ans);
    }
    return 0;
}

3. 1010-HDU 5802 Windows 10

题意:windows 10 调音量。

按up键可以往上调高一个DB,按down键可以向下调x个DB,如果上一次是按的up键或者上一次是停止,x=1,如果上次按的是down键,且向下降了xDB,则x=2*x。要求从p调到q,最少需要多少次按键?

题解:

显然,如果p<=q,则只能一次一次向上调,答案为q-p;

如果p>q,那么最少的步数为:

一直指数下降,下降到大于q的最小的那个数,然后停一次,再继续指数下;

或者下降到小于q的最大的那个数,然后在向上升回去。

比较两种情况所需的步数,取最小值。

需要注意的是:

1. 音量不能小于0;

2. 由于up和停止都可以使得x变为1,所以可以在用上升来抵消停顿,即在需要停一次的时候向上升1DB,这样可以使得下降到比q小时少上升几次。

代码:

#include <iostream>
#include <algorithm>
#include <cstdio>
using namespace std;
typedef long long LL;

LL dfs(LL s,LL t,LL step,LL rest)
{
    if(s==t) return step;
    LL x=0;
    while(s-(1<<x)+1>t) x++;
    if(s-(1<<x)+1==t) return step+x;
    LL up=t-max(s-(1<<x)+1,0ll);
    return min(step+x+max(0ll,up-rest),dfs(s-(1<<(x-1))+1,t,step+x,rest+1));
}
int main()
{
    int T;
    scanf("%d",&T);
    while(T--)
    {
        int p,q;
        scanf("%d%d",&p,&q);
        if(p<=q) printf("%d\n",q-p);
        else printf("%I64d\n",dfs(p,q,0,0));
    }
    return 0;
}


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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值