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 ,那么接下来就是我们用BSGS算法求解的过程.

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

首先我们设 m = p , 然后我们设 y = i * m + j; 那么 xy = xmi+j , 其中 0 i, j < m,那么我们只需要枚举 i, 余数复杂度降为 p . 于是原式可以写成: xj = xim * 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;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值