UVA 11916 Emoogle Grid(模指数方程)

题意:用k种不同颜色给m*n方格涂色。其中有b个点不用涂色,给出这b个点的位置。满足上下相邻的两个位置不能涂相同颜色。
已知列数n和涂色总方案r(已经对mod取模)。求最小的行数m
解法:
1.对于m*n列方格,k中不同颜色涂色,且上下不能涂相同颜色,共:
(k*(k-1)^(m-1))^n中染色方案数
2.有若干格子不能染色,则可以跳过格子进行类似统计。得到前m1行的染色数目和第m+1行的染色数目之和cnt(m1是不用涂色的格子中最大的行数),以后每增加一行cnt*=(k-1)^n,令(k-1)^n为p,则cnt*p^(ans-m-1)=r (mod),p^(ans-m-1)=r *inv(cnt)(mod),即解一个模指数方程,使用Baby-Step算法。

#include<stdio.h>
#include<iostream>
#include<string.h>
#include<algorithm>
#include<map>
#include<vector>
#include<queue>
#include<stack>
#include<math.h>
#include<set>
#define MP make_pair
#define ll long long
using namespace std;

const ll mod = 100000007 ;
const ll maxn = 500+10 ;

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

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;
}

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

ll pow_mod(ll a,ll b){
    ll ans=1;
    while(b){
        if(b&1) ans=mul_mod(ans,a),b--;
        a=mul_mod(a,a),b>>=1;
    }
    return ans;
}

//统计不变部分的涂色种类数
ll Count(){
    ll c=0;
    //统计不能涂色格子下的可涂k种颜色的格子数
    for(ll i=0;i<b;i++){
        if(x[i]!=m&&!best.count(MP(x[i]+1,y[i]))) c++;
    }
    //统计第一行的可涂k种颜色的格子数
    c+=n;
    for(ll i=0;i<b;i++) if(x[i]==1) c--;
    return mul_mod(pow_mod(k,c),pow_mod(k-1,m*n-b-c));
}

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

ll doit(){
    ll cnt=Count();
    if(cnt==r) return m;
    ll c=0;
    for(ll 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(){
    //freopen("a.txt","r",stdin);
    scanf("%lld",&t);
    for(ll cas=1;cas<=t;cas++){
        scanf("%lld%lld%lld%lld",&n,&k,&b,&r);
        best.clear();
        m=1;
        for(ll i=0;i<b;i++){
            scanf("%lld%lld",&x[i],&y[i]);
            if(x[i]>m) m=x[i];
            best.insert(MP(x[i],y[i]));
        }
        printf("Case %lld: %lld\n",cas,doit());
    }
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值