Emoogle Grid UVA - 11916(BSGS)

Emoogle Grid UVA - 11916

这道题需要求离散对数取模,具体的求解算法是BSGS算法
如果不了解,可以先看一下网上一篇博客写的很清楚

BSGS算法

题目意思:

题意:要给M行N列的网格染上K种颜色,其中有B个不用染色,其他每个格子涂一种颜色,同一列上下两个格子不能染相同的颜色,给出M,N,K,和B个格子的位置,求出涂色的方案%100000007的结果是R,现在给出N,K,R和B个格子位置,让你求最小的M

题目分析:

我们尝试着一列一列的染色,发现这道题的染色方法其实非常简单,因为它只有两个限定条件
1.上下两个相邻格子不能同色
2. 有B个不能涂色到格子

因此涂色方案也非常到好找,我们发现每个可以涂色的格子,其可能涂色方案数要么是k,要么是k-1。
1、当在第一行时,因为不受上一行影响(没有上一行),能涂色到格子的方案数是k种
2、当恰好在不能涂色到格子下一个时,因为不受上一个不能涂色到格子的影响,这个格子的方案数是k种
3、其他能够涂色到格子到种类都是k-1种

我们可以比较容易想到的是M最起码要和B个格子中X坐标最大的一样假设这个最大行是max,而且根据上面的分析我们可以发现,从max行往上的这部分所有格子的方案数其实是固定的,我们完全可以算出来,即算出k-1种的格子多少个,k种到格子多少个,乘起来就是这部分的总方案数即 c n t = k c 1 ( k 1 ) c 2 ,这个时候我们可以检测一下是否和所给答案相同,相同直接输出答案。

如果不同:
如上分析,max行往下到部分是变化的,首先第max+1行,受到max行中不能涂色到格子的影响,所以我们单独算出来这一行的方案数num,然后乘到之前部分的方案数中,即 c n t = c n t n u m ,这个时候我们在检查是否符合答案,符合输出。

如果不同:
下面就简单了,每一行都是 ( k 1 ) n 种,n是列,每个格子都受上一行影响,所以是k-1种。设 a = ( k 1 ) n ,我们设还需要增加M行,则这M行的格子的总的种类数就是 a a . . . a = a M ,再乘上原来那部分种类数我们就得到了同余式
c n t a M R   m o d (   m o   )

a M R c n t 1   m o d (   m o   ) (其中 c n t 1 是cnt的逆元)

这样我们到任务就是求M,这就是离散对数取模,需要用BSGS算法

我们继续设 b = R c n t 1

得到 a M b   m o d (   m o   )

扫描二维码关注公众号,回复: 2207647 查看本文章

上面我贴了一篇博客网址,其算法步骤如下:
1 设 M = i × m + j
其中 m = m o   , i = M m , j = M % m

2 先枚举 j ( j [ 0 , m ] ) 算出 a j 用hash存下来 并记录对应j

因为 a M = ( a m ) i a j a j 已经枚举完并记录下来了

3 枚举i, a m 可以提前算出来,所以每次枚举i算出 ( a m ) i = A
A a j b   m o d ( m o )
所以每次枚举为们就可以用扩展欧几里徳求同余式方程求出 a j ,然后在已经存好的hash中查找是否存在,存在就说明我们找到了,这样i,j都有了我们就求出了 M = i × m + j

以上便是这个算法的完整过程


但是我们会发现一个问题,就是枚举i的过程中,每次都需要用扩展欧几里徳求一遍感觉有些麻烦可以简化一些吗?

我们观察一下第三步,看有什么可以改进到地方
第三步中我们需要求
A a j b   m o d ( m o )

因为第一个式子原式是 ( a m ) i a j b   m o d ( m o )
枚举i每增加一相当于左边多乘了一个 a m
那么是不是就相当于每次右边多乘一个 ( a m ) 1

x b A 1   m o d ( m o )
这样我们一开始只需要求出A的逆元,即最初的
a m 1 即可
之后for循环枚举中i每增加1,就b就多乘一次逆元即可,就不用每次用扩展欧几里徳解同于式了。

code:

#include <cstdio>
#include <iostream>
#include <cstring>
#include <algorithm>
#include <map>
#include <set>
#include <cmath>
typedef long long ll;
using namespace std;
const int maxn = 510;
const ll mod = 100000007;

int n, m, k, b, r, x[maxn], y[maxn];
set<pair<int, int> > best;

ll mul_mod(ll a, ll b) {
    return  a * b % mod;
}

ll pow_mod(ll a, ll p) {
    ll tmp = 1;
    while (p) {
        if (p & 1)
            tmp = tmp * a % mod;
        p >>= 1;
        a = a * a % mod;
    }
    return tmp;
}

void gcd(ll a, ll b, ll &d, ll &x, ll &y) {
    if (!b) {
        d = a;
        x = 1;
        y = 0;
    }
    else {
        gcd(b, a%b, d, y, x);
        y -= x*(a/b);
    }
}

ll inv(ll a) {
    ll d, x, y;
    gcd(a, mod, d, x, y);
    return d == 1 ? (x+mod) % mod : -1;
}

int log_mod(int a, int b) {
    int m, v, e = 1, i;
    m = (int)sqrt(mod+0.5);
    v = inv(pow_mod(a, m));
    map<int, int> x;
    x[1] = 0;
    for (int i = 1; i < m; i++) {
        e = mul_mod(e, a);
        if (!x.count(e))
            x[e] = i;
    }
    for (int i = 0; i < m; i++) {
        if (x.count(b))
            return i*m + x[b];
        b = mul_mod(b, v);
    }
    return -1;
}

int count() {
    int c = 0;
    for (int i = 0; i < b; i++)
        if (x[i] != m && !best.count(make_pair(x[i]+1, y[i])))
            c++;//不能涂色的格子下面一个格子有k种

    c += n;//第一行如果都能涂色有k种
    for (int i = 0; i < b; i++)
        if (x[i] == 1)
            c--;//除去第一行中不能涂色的

    return mul_mod(pow_mod(k, c), pow_mod(k-1, (ll)m*n-b-c));//固定部分总的减去不能涂的,减去k种的就是k-1种的
}

int doit() {
    int cnt = count();
    if (cnt == r)
        return m;

    int c = 0;
    for (int i = 0; i < b; i++)
        if (x[i] == m)
            c++;
    m++;
    cnt = mul_mod(cnt, pow_mod(k, c));
    cnt = mul_mod(cnt, pow_mod(k-1, n-c));
    if (cnt == r)
        return m;

    return log_mod(pow_mod(k-1, n), mul_mod(r, inv(cnt))) + m;
}

int main() {
    int t, cas = 1;
    scanf("%d", &t);
    while (t--) {
        scanf("%d%d%d%d", &n, &k, &b, &r);
        best.clear();
        m = 1;
        for (int i = 0; i < b; i++) {
            scanf("%d%d", &x[i], &y[i]);
            if (x[i] > m)
                m = x[i];
            best.insert(make_pair(x[i], y[i]));
        }
        printf("Case %d: %d\n", cas++, doit());
    }
    return 0;
}

猜你喜欢

转载自blog.csdn.net/codeswarrior/article/details/81089307