涉及知识点: 逆元,组合数优化,欧拉降幂,分式化简,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互质,则:
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; }