[大步小步算法] UVA 11916

题意

给m × n 格子,k种涂色,上下颜色不能相同,b个不许图的格子(坐标),已知总共种类数的r,求n

思路

因为题目要求同列相邻两格不同色,所以列与列之间不影响,可以逐列染色。

如果一个格子的上面相邻的格子,已经被染色则染这个格子的时候,共有k-1中选择。

反过来,如果一个格子位于第一列,或者上面相邻的格子是不能被染色的格子,则共有k中选择。

虽然,矩阵的行数不定,但至少为所有不能被染色格子行标的最大值m。

分别检验一下染m行和m+1行的方案数(mod 100000007)是否为r

否则的话,后面每染一行方案数都会乘p = (k-1)n,假设前面计算出来的m+1行方案数为cnt

下面的任务就是求解 px * cnt = r (mod MOD),两边乘cnt的逆,得 px = r * cnt-1 (mod MOD)

最后加上前面的m+1行,答案为x + m + 1

代码

// http://xwk.iteye.com/blog/2130842
// https://en.wikipedia.org/wiki/Baby-step_giant-step
// http://www.cnblogs.com/AOQNRMGYXLMV/p/4322668.html

#include <cmath>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <map>
#include <set>
#include <vector>

using namespace std;

typedef long long ll;

const ll MOD = 100000007;
const ll maxb = 510;

int m, n, k, b, r;
int x[ maxb ], y[ maxb ];
set<pair<ll, ll>> blockSet;

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

//因为一堆的ll和ll导致WA和TLE
//全换成ll可以AC
ll pow_mod ( ll a, ll x ) {
    ll ans = 1, base = a;
    while ( x ) {
        if ( x & 1 )
            ans = mul_mod ( ans, base );
        base = mul_mod ( base, base );
        x >>= 1;
    }
    return ans;
}

inline ll inverse ( ll a ) { return pow_mod ( a, MOD - 2 ); }

//求 k 使 a^k === b
//即 b * ( a ^ -m ) ^ i === a ^ j
//计算一组( n ^ 1/2 个 )a^j,然后不断给左半部分升幂来验证两边是否相等
ll log_mod ( ll a, ll b ) {
    ll m = sqrt ( MOD + 0.5 );
    ll am = inverse ( pow_mod ( a, m ) ); // a ^ -m

    map<ll, ll> aj; // a ^ j
    aj[ 1 ] = 0;

    ll e = 1;
    // Compute αj and store the pair (j, αj) in a table
    for ( ll j = 1; j < m; ++j ) {
        e = mul_mod ( e, a );
        if ( e == b )
            return j;
        if ( !aj.count ( e ) )
            aj[ e ] = j;
    }

    // Check to see if b' is the second component (αj) of any pair in the table.
    for ( ll i = 0; i < m; ++i ) {
        if ( aj.count ( b ) )
            return i * m + aj[ b ];
        b = mul_mod ( b, am );
    }
    return -1;
}

ll solve ( ll brow ) {

    // how many k in m rows
    ll c = 0;
    for ( ll i = 0; i < b; ++i ) {
        //不超界,下面的格子不被屏蔽
        if ( x[ i ] + 1 <= m && !blockSet.count ( make_pair ( x[ i ] + 1, y[ i ] ) ) )
            ++c;
    }
    // 第一行k个的个数
    c += n - brow;
    ll cnt = mul_mod ( pow_mod ( k, c ), pow_mod ( k - 1, (ll)m * n - b - c ) );
    if ( cnt == r )
        return m;

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

    ll p = pow_mod ( k - 1, n );
    ll v = inverse ( cnt );
    ll sol = log_mod ( p, mul_mod ( r, v ) );
    return sol + m + 1;
}

int main () {
    int T;
    scanf ( "%d", &T );
    for ( int ks = 1; ks <= T; ++ks ) {
        scanf ( "%d%d%d%d", &n, &k, &b, &r );

        blockSet.clear ();
        m = 1;
        ll blockRow = 0;
        for ( ll i = 0; i < b; ++i ) {
            scanf ( "%d%d", &x[ i ], &y[ i ] );
            blockSet.insert ( make_pair ( x[ i ], y[ i ] ) );
            if ( x[ i ] > m )
                m = x[ i ];
            if ( x[ i ] == 1 )
                blockRow++;
        }

        int sol = solve ( blockRow );
        printf ( "Case %d: %d\n", ks, sol );
    }
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值