题意:
给 m * n 的矩阵涂色,要求上下相邻的格子不能涂一样的颜色,其中有 b 个格子不能涂色,给出涂色的方案数 r 和 列数 n,求这个矩阵的行数 m
分析:
不难发现,每一列被不能涂色的格子分成了几块,那么就有几个格子能涂 k 种颜色,剩余的只能涂 k-1 种颜色,设 m = max(不能涂色的 x ),m 和 m+1以后的情况是不一样的,因为 m+1 后有的列会多出一块,特判一下,用BSGS求解即可
疑点:
当 b = 0 时,有:,BSGS求解 x 即可,但会WA,必须要特判 K^n = r 时,最后答案为 1。这个特判和求扩展BSGS时的特判类似,必须特判一下 K^n = r,m 和 m+1 也要特判,因为K的指数会不一样。我始终不能理解这个为什么要特判,望路过的大佬指点迷津
代码:
#include <map>
#include <set>
#include <cmath>
#include <cstdio>
#include <iostream>
#define fi first
#define se second
#define mk make_pair
#define LL long long
#define pii pair<LL,LL>
using namespace std;
const int mod = 100000007;
LL n,k,b,r,a,d;
LL qpow(LL a,LL x)
{
LL res = 1;
while(x)
{
if(x&1) res = res * a % mod;
a = a * a % mod;
x >>= 1;
}
return res;
}
LL Bsgs()
{
LL t = ceil(sqrt(mod*1.0));
map<LL,LL> p;
for(int j = 1; j <= t ; ++j)
{
r = r * a % mod;
p[r] = j;
}
LL v = qpow(a,t);
for(int i = 1; i <= t+1; ++i)
{
d = d * v % mod;
if(p[d])
return i * t - p[d];
}
return -1;
}
int main()
{
int t; scanf("%d",&t);
for(int cases = 1; cases <= t; ++cases)
{
scanf("%lld %lld %lld %lld",&n,&k,&b,&r);
set<pii> s;
LL x,y,M = -1;
for(int i = 0; i < b; ++i)
{
scanf("%lld %lld",&x,&y);
M = max(M,x);
s.insert(mk(x,y));
}
printf("Case %d: ",cases);
set<pii>::iterator it = s.begin();
if(b>0)
{
LL sum = n,num = 0; //块数、x = M的数量
for(; it != s.end(); ++it)
{
pii p = *it;
if(p.fi != M && !s.count(mk(p.fi+1,p.se))) sum++;
if(p.fi == 1) sum--;
else if(p.fi == M ) num++;
}
LL rr = qpow(k,sum) * qpow(k-1,n*M-sum-b) % mod;
if(rr == r) //特判 M
{
printf("%lld\n",M);
continue;
}
M = M + 1;
rr = rr * qpow(k,num) % mod;
rr = rr * qpow(k-1,n-num) % mod;
if(rr == r) //特判 M + 1
{
printf("%lld\n",M);
continue;
}
a = qpow(k-1,n);
d = rr;
printf("%lld\n",M+Bsgs());
}
else
{
LL rr = qpow(k,n);
if(rr == r) printf("1\n"); //特判
else
{
d = rr;
r = r * qpow(k-1,n) % mod;
a = qpow(k-1,n);
printf("%lld\n",Bsgs());
}
}
}
return 0;
}