UVa11916 - Emoogle Grid(bsgs)

71 篇文章 0 订阅

题目链接

简介:
有这样一个题目:给定一个M*N的网格涂上K种颜色,其中B个格子不能涂色,其他每个格子涂一种颜色,同一列中的上下两个相邻格子不能涂相同颜色
给出M,N,B个格子的位置,求出涂色方案R%100000007

这道题是上述问题的逆问题:给出N,K,R,B个格子的位置,
求出最小的M

分析:
虽然M(行数)是未知的,但是我们可以知道ta的最小值:
M=max{不能涂色的格子的行编号}
因此可以把整个网格分成两部分:不可变部分,可变部分
这里写图片描述
我们一列一列的进行涂色
如果一个格子上一个位置不能涂色,那么ta的颜色就有K种选择
其余的格子都只有K-1种选择

假设不变部分以及可变部分的第一行可行的涂色方案有cnt种
(cnt的求解要稍微麻烦一点)
那么每增加一行,涂色方案都会乘P=(K-1)^N
这样我们就得到一个模方程:

cnt*P^M=R

稍微变形得到:

P^M=R*cnt^-1

直接上bsgs求解即可

tip

B=0的时候需要特殊求解
时刻判断cnt是不是已经等于r了

交上去是T,拍也拍不起来(数据不好造)

//这里写代码片
#include<cstdio>
#include<cstring>
#include<iostream>
#include<map>
#include<set>
#include<cmath>
#define ll long long

using namespace std;

const ll mod=100000007;
map<ll,int> mp;
int n,m,k,r,b,x[1000],y[1000];
set<pair<int,int> > po;              //方便判断

ll KSM(ll x,ll y)
{
    ll t=1;
    x%=mod;
    while (y)
    {
        if (y&1) 
           t=(t%mod*x%mod)%mod;
        y>>=1;
        x=(x%mod*x%mod)%mod;
    }
    return t%mod;
}

int bsgs(ll x,ll z)                  //x^y=z
{
    x%=mod; z%=mod;
    if (x==0&&z==0) return 1;
    if (x==0) return -1;

    mp.clear();
    ll now=1;
    ll m=(ll)ceil(sqrt((double)mod));
    mp[1]=m+1;
    for (int i=1;i<m;i++)
    {
        now=(now%mod*x%mod)%mod;
        if (!mp[now]) mp[now]=i;     //x^i=now
    }

    ll inv=1,tmp=KSM(x,mod-m-1);
    for (int k=0;k<m;k++)
    {
        int i=mp[(z*inv)%mod];       //z*x^-km
        if (i)
        {
            if (i==m+1) i=0;
            return k*m+i;
        }
        inv=(inv%mod*tmp%mod)%mod; 
    }
    return -1;
}

ll count()
{
    int i,j;
    int c1=0;
    for (int i=1;i<=b;i++)
        if (x[i]!=m&&!po.count(make_pair(x[i]+1,y[i])))   //计算不可涂色的方格下面有多少不受限制的方格 
            c1++;                                         //其中不包含行数大于m的不受限制方格 

    c1+=n;                                                //第一行都有k种涂法   
    for (int i=1;i<=b;i++)
        if (x[i]==1) c1--;                                //去除不能涂色的方块
    int c2=n*m-c1-b;                                      //有k-1种涂色方法的方格

    //a=k^c1 * (k-1)^c2 方案数 
    ll a=(KSM((ll)k,(ll)c1) * KSM((ll)(k-1),(ll)c2))%mod; 
    return a;
}

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

    int c=0;
    for (int i=1;i<=b;i++)
        if (x[i]==m) c++;                                   //可变部分中不受限制的方格数 
    cnt=(cnt * KSM((ll)k,(ll)c))%mod;
    cnt=(cnt * KSM((ll)(k-1),(ll)(n-c)))%mod;
    if (cnt==r) return m+1;

    m++;
    ll P=KSM((ll)(k-1),(ll)n);
    ll rr=(r*1LL*KSM(cnt,mod-2))%mod;
    return bsgs(P,rr)+m;
}

int main()
{
    int T;
    scanf("%d",&T);
    for (int cas=1;cas<=T;cas++)
    {
        m=0;
        scanf("%d%d%d%d",&n,&k,&b,&r);
        for (int i=1;i<=b;i++)
        {
            scanf("%d%d",&x[i],&y[i]);
            po.insert(make_pair(x[i],y[i]));
            if (x[i]>m) m=x[i];
        }

        if (b==0)
        {
            ll cnt=KSM((ll)k,(ll)n);
            if (cnt==r) m=1;
            else
            {
                ll P=KSM((ll)(k-1),(ll)n);
                ll rr=(r*1LL*KSM(cnt,mod-2))%mod;
                m=bsgs(P,rr)+1;
            }
            printf("Case %d: %d\n",cas,m);      
        }
        else printf("Case %d: %d\n",cas,doit());    
    }
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值