哈尔滨理工大学第12届程序设计竞赛(牛客)题解

 涉及知识点: 逆元,组合数优化,欧拉降幂,分式化简,DFS

B题:


思路:

        显然概率分析有P=/;因为q次的概率其实是相同的(有放回),对其概率求逆元有:                                                qpow(C(n,k),q,mod)*qpow(C(n+m,k),mod-2,mod); 

这里我们使用到了快速幂;其次我们要注意的是求组合数的时候,我发现一开始老是T了;

错误组合数代码:

long long FA(long long a) //定义阶乘函数FA
{
    long long b=1;//定义变量b
    for(int i=1;i<=a;i++)//计算阶乘
        b=(q_mul(b,i,mod))%mod;
    return b;//返回值得到b=a!
}
int F(int n,int k)
{
    return (FA(n)%mod)/(FA(k)*FA(n-k))%mod;
}

这里我使用该效率高的组合数代码:

const int maxn = 1e6 + 5;
 
int f(int n,int m){
    static int M=0,inv[maxn],mul[maxn],invMul[maxn];
    while(M<=n){
        if(M){
            inv[M]=M==1?1:(mod-mod/M)*inv[mod%M]%mod;
            mul[M]=mul[M-1]*M%mod;
            invMul[M]=invMul[M-1]*inv[M]%mod;
        }
        else mul[M]=1,invMul[M]=1;
        M++;
    }
    return mul[n]*invMul[m]%mod*invMul[n-m]%mod;
}

这里给出AC代码:

#include<queue>
#include<iostream>
#include<algorithm>
#include<string.h>
#include<cstdio>
#include <map>
#include<string>
#include<cstring>
#include<iomanip>
#include<set>
#include<map>
#include<numeric>
#include<math.h>
using namespace std;
#define int  long long
typedef unsigned long long ull;
typedef double db;
typedef long double ldb;
#define heap priority_queue
#define pll pair<long long, long long>
#define fir first
#define sec second
#define SPO(n) fixed <<setprecision(n)
#define FOR(i, l, r) for (long long i = l; i <= r; ++i)
#define ROF(i, r, l) for (long long i = r; i >= l; --i)
#define endl '\n'
const double pi=3.1415926535;
const double EPS = 1e-9;
const long long MAX = 1e9+5;
int  n,sum,m,q,k;
const int mod=1e9+7;


unsigned int q_mul(int a, int b, int mod)//快速乘法
{
    unsigned int ans = 0;
    while (b)
    {
        if (b & 1)
        {
            b--;
            ans = (ans + a) % mod;
        }
        b >>= 1;
        a = (a + a) % mod;
    }
    return ans;
}
int qpow(int a, int n, int mod)//快速幂
{
    int ans = 1;
    while (n)
    {
        if (n & 01)ans = ans * a % mod;
        a = a * a % mod;
        n >>= 1;
    }
    return ans;
}
//求组合数!!!效率高!!!!
const int maxn = 1e6 + 5;

int f(int n,int m){
    static int M=0,inv[maxn],mul[maxn],invMul[maxn];
    while(M<=n){
        if(M){
            inv[M]=M==1?1:(mod-mod/M)*inv[mod%M]%mod;
            mul[M]=mul[M-1]*M%mod;
            invMul[M]=invMul[M-1]*inv[M]%mod;
        }
        else mul[M]=1,invMul[M]=1;
        M++;
    }
    return mul[n]*invMul[m]%mod*invMul[n-m]%mod;
}

void Solve(void) {
    cin>>n>>m>>k>>q;
    if(n==0||k>n)
    {
        cout<<0<<endl;
        return ;
    }
    int ans1=f(n,k);
    int ans2=f(n+m,k);
    ans1= qpow(ans1,q,mod);
    ans2= qpow(ans2,q,mod);
    cout<<ans1*qpow(ans2,(mod-2),mod)%mod<<endl;
    return ;
}

signed main() {
    std::ios::sync_with_stdio(false);
    cin.tie(0);cout.tie(0);
    int t;
    cin>>t;
    while(t--)
    Solve();

}

 J题:


题解

         这里我们发现 其实就是 欧拉降幂!!!当然我们只需要特判0^0=1就可以了(前提是要学过欧拉特判),这里我们给出C语言代码:

上AC代码:

#include <bits/stdc++.h>
#define ll long long
#define mod 10000000007
using namespace std;
char a[1000006];
ll x,z;
ll quickpow(ll x,ll y,ll z)
{
    ll ans=1;
    while(y)
    {
        if(y&1)
            ans=ans*x%z;
        x=x*x%z;
        y>>=1;
    }
    return ans;
}
ll phi(ll n)
{
    ll i,rea=n;
    for(i=2;i*i<=n;i++)
    {
        if(n%i==0)
        {
            rea=rea-rea/i;
            while(n%i==0)
                n/=i;
         }
    }
    if(n>1)
        rea=rea-rea/n;
    return rea;
}
int main()
{
	int t;
	scanf("%d",&t);
	while(t--)
	{
		scanf("%lld %s %lld",&x,a,&z);
		ll len=strlen(a);
        ll p=phi(z);
        ll ans=0;
        for(ll i=0;i<len;i++)
            ans=(ans*10+a[i]-'0')%p;
        ans+=p;
        printf("%lld\n",quickpow(x,ans,z));
        
	}
    return 0;
}

C题:


 题解;

       因为没有障碍物,我们可以直接坐标差最小

#include<queue>
#include<iostream>
#include<algorithm>
#include<string.h>
#include<cstdio>
#include <map>
#include<string>
#include<cstring>
#include<iomanip>
#include<set>
#include<map>
#include<numeric>
#include<math.h>
using namespace std;
#define ll  long long
typedef unsigned long long ull;
typedef double db;
typedef long double ldb;
#define heap priority_queue
#define pll pair<long long, long long>
#define fir first
#define sec second
#define SPO(n) fixed <<setprecision(n)
#define FOR(i, l, r) for (long long i = l; i <= r; ++i)
#define ROF(i, r, l) for (long long i = r; i >= l; --i)
#define endl '\n'
const double pi=3.1415926535;
const double EPS = 1e-9;
const long long MAX = 1e5+5;
int  n,sum,m,q,k;

int dx[7]={1,-1,0,0,0,0};
int dy[7]={0,0,1,-1,0,0,};
int dz[7]={0,0,0,0,1,-1};
void Solve()
{
    int n,m,q,op,x,h,y,z;
    int st=0,mp[4][MAX],s;
    cin>>n>>m>>h>>q;
    while(q--)
    {
        cin>>op>>x>>y>>z;
        if(op==1)
        {
            mp[0][st]=x,mp[1][st]=y,mp[2][st]=z;
            st++;continue;
        }else
        {
            s=1e5;
            for(int i=0;i<st;++i)
            {
                int k=abs(mp[0][i]-x)+ abs(mp[1][i]-y)+abs(mp[2][i]-z);
                if(s>k)s=k;
            }
            cout<<s<<endl;
        }
    }
}
signed main() {
    std::ios::sync_with_stdio(false);
    cin.tie(0);cout.tie(0);
    int t;
    cin>>t;
    while(t--)
        Solve();

}

但是,我们为了熟悉bfs也可以这个样子

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll MAXN=5e5+5;
ll n,m,h,qw;
struct pp
{
    ll x,y,z,dis;
};
ll change(ll x,ll y,ll z)
{
    return x-1+(y-1)*n+(z-1)*n*m;
}
ll vis[MAXN];
ll dx[10]={1,0,0,-1,0,0};
ll dy[10]={0,1,0,0,-1,0};
ll dz[10]={0,0,1,0,0,-1};
ll ans[MAXN];
ll mnx=1e18,mxx=0,mny=1e18,mxy=0,mnz=1e18,mxz=0;
bool check(ll x,ll y,ll z)
{
    return x >= 1 && x <=n  && y >= 1 && y <= m && z >= 1 && z <= h;
}
queue<pp>q;
void bfs()
{
    while(!q.empty())
    {
        pp ro=q.front();
        q.pop();
        for(int i=0;i<6;++i)
        {
            ll tx=ro.x+dx[i];
            ll ty=ro.y+dy[i];
            ll tz=ro.z+dz[i];
            if(!check(tx,ty,tz))continue;
            if(vis[change(tx,ty,tz)]>ro.dis+1) {
                q.push({tx, ty, tz, ro.dis + 1});
                vis[change(tx,ty,tz)]=ro.dis+1;
            }
        }
    }
}
int main()
{
    ll t;
    cin>>t;
    while(t--)
    {
        cin>>n>>m>>h>>qw;
        for(int i=0;i<=n*m*h;++i)vis[i]=1e18;
        for(int i=0;i<qw;++i)
        {
            ll op,x,y,z;
            scanf("%lld %lld %lld %lld",&op,&x,&y,&z);
            if(op==1)
            {
                q.push({x,y,z,1});
                vis[change(x,y,z)]=1;
            }
            else {
                bfs();
                printf("%lld\n", vis[change(x, y, z)] - 1);
            }
        }
    }
}

 


补充知识点:

 1.欧拉降幂:

求a^b(mod p)的值 ,当b很大很大很大很大很大的时候,可以使用欧拉降幂

欧拉定理:若n,a为正整数,且n,a互质,则:{​{\color{Red} }a^{\phi (n)}\equiv 1(mod n)}

2.C++逆元

                       qpow(C(n,k),q,mod)*qpow(C(n+m,k),mod-2,mod); 

即 分子*qpow(分母,mod-2,mod);      (1/4的逆元为250000002;

3.分式化简

/*分数化简,关键是求出分母和分子的最大公因数。
  这里采用“辗转相除法”求两个整数的最大公因数。
  辗转相除法, 又名欧几里德算法(Euclidean algorithm),是求最大公约数的一种方法。
  它的具体做法是:用较大数除以较小数,再用出现的余数(第一余数)去除除数,再用出现的余数(第二余数)去除第一余数,
  如此反复,直到最后余数是0为止。如果是求两个数的最大公约数,那么最后的除数就是这两个数的最大公约数。
*/
 
#include<iostream>
 
using namespace std;
 
int main() {
	int n, m;	
	cout << "请分别输入分母和分子:" << endl;
	cin >> m >> n;
	int m0 = m, n0 = n;    //m0和n0保存m和n的原始值
	while (m%n != 0) {
		int temp = m ;
		m = n;
		n = temp%n;
	}
	cout << "原分式的最简分式为: " << n0 / n << "/" << m0 / n << endl;		                                                   
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值