CC

自然万物都趋向从有序变得无序

0%

UVA 11916 Emoogle Grid

题目链接:点我

题目


题意:

给你一个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 ,我们的填充方案数就应该乘以 (K1)M{(K - 1)} ^{ M},那么接下来就是我们用BSGS算法求解的过程.

BSGS: 先看一个式子 xyx^ { y} ≡ z (mod p),p是质数,现在只知道x和 z 和 p , 要求 y . 大步小步算法(BSGS,Baby Steps Giant Steps)就是解决这个问题的,当然还有扩展的大步小步算法可以解决 p 不是素数的情况.

首先我们设 m = p\sqrt p, 然后我们设 y = i * m + j; 那么 xyx^ { y} = $ x ^{ m * i + j}$, 其中 0 \le i, j <\lt m,那么我们只需要枚举 i, 余数复杂度降为 p\sqrt p. 于是原式可以写成: xjx^{j} = ximx^{- i * m} * z,于是我们找到xmx^{ m}的逆元,然后枚举即可,

代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
#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;
}