题目链接:点我
题意:
给你一个m * n的矩阵, 矩阵上有 B 个点不可填充,剩下的点填 K 种 颜色,现在给你矩阵的 N ,和 R ,R代表这个矩阵用这 K 种颜色填充共有多少种不同的方案.让你求矩阵的 N .
思路:
核心难点是大步小步算法(Baby Steps Giant Steps).首先我们先找出矩阵不能填的点的最大纵坐标maxy,然后判断以maxy 为 N 是否符合条件,如果不符合我们再判断以 maxy + 1 为 N ,判断是否符合条件,如果还是不符合,那么接下来我们算出此时以 maxy + 1 为 N 的 矩阵填充共有多少种不同的方案,然后我们知道,在 maxy + 1 的基础上 N 没增加 1 ,我们的填充方案数就应该乘以 (K−1)M ,那么接下来就是我们用BSGS算法求解的过程.
BSGS: 先看一个式子 xy ≡ z (mod p),p是质数,现在只知道x和 z 和 p , 要求 y . 大步小步算法(BSGS,Baby Steps Giant Steps)就是解决这个问题的,当然还有扩展的大步小步算法可以解决 p 不是素数的情况.
首先我们设 m = p√ , 然后我们设 y = i * m + j; 那么 xy = xm∗i+j , 其中 0 ≤ i, j < m,那么我们只需要枚举 i, 余数复杂度降为 p√ . 于是原式可以写成: xj = x−i∗m * z,于是我们找到 xm 的逆元,然后枚举即可,
代码:
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<iostream>
#include<cmath>
#include<map>
#include<set>
using namespace std;
typedef long long LL;
const int mod = 1e8 + 7;
set<pair<int, int> > best;
int x[510],y[510];
LL qpow(LL x,LL n){
LL ans = 1;
while(n){
if(n & 1) ans = ans * x % mod;
x = x * x % mod;
n >>= 1;
}
return ans;
}
void exgcd(LL a,LL b, LL &d, LL &x, LL &y){//扩展欧几里得算法求逆元,
if(!b){x = 1; y = 0;d = a; return ;}
exgcd(b, a %b, d, y, x);
y-= a/b * x;
}
LL inv(LL a){
LL x, y,d;
exgcd(a,mod,d,x,y);
return d == 1 ? (x + mod ) % mod : -1;
}
int BSGS(LL x,LL r){
LL m =(LL)ceil(sqrt((double)mod+0.5));
// LL v = qpow(qpow(x,mod-2),m);
LL v = inv(qpow(x,m));//经验证,两种方法都可以求得正确的逆元
map<LL,LL> q;
map<LL,bool> vis;
LL k = 1;
vis[k] = 1;
q[k] = 0;
for(int i = 1; i < m; ++i){//枚举x 的次方
k = k * x % mod;
if(vis[k] == 0){
vis[k] = 1;
q[k] = i;
}
}for(int i = 0; i< m; ++i){
if(vis[r]){//如果左右两边都已经出现了,那么说明找到解.
int ans = i * m +q[r];
return ans;
}
r =r * v % mod;//每次乘以逆元,即是在算我们上述的 x ^(- i* m) * z;
}
return -1;
}
int main(){
int t;
scanf("%d", &t);
int kase = 0;
while(t--){
LL b, n, k, r,num;
int maxx;
scanf("%I64d %I64d %I64d %I64d", &n, &k, &b, &r);
maxx = 1;num = 0;best.clear();
for(int i = 1; i <= b ; ++i){
scanf("%I64d %I64d", &x[i], &y[i]);
maxx= max(maxx,x[i]);//找到最大的x,
best.insert(make_pair(x[i],y[i]));
}for(int i= 1; i <= b; ++i)
if(x[i] != maxx && !best.count(make_pair(x[i]+1,y[i])))
++num;
for(int i = 1; i <= b;++i)
if(x[i] == 1)
--num;
num += n;
LL sum = (qpow(k,num) * qpow(k - 1, maxx * n - b - num) ) % mod;
if (sum == r) {//判断是否符合条件,
printf("Case %d: %d\n",++kase, maxx);
continue;
}num = 0;
for(int i = 1; i <= b; ++i)
if(x[i] == maxx)
++num;
++maxx;
sum = sum * qpow(k-1,n-num) % mod;
sum = sum * qpow(k,num) % mod;
if (sum == r){
printf("Case %d: %d\n",++kase, maxx );
continue;
}LL x = qpow(k-1,n);
r = r *inv(sum) % mod;
int ans = maxx + BSGS(x,r);
printf("Case %d: %d\n",++kase,ans);
}return 0;
}