Bzoj 2242: [SDOI2011]计算器(BSGS)

2242: [SDOI2011]计算器
Time Limit: 10 Sec Memory Limit: 512 MB
Description
你被要求设计一个计算器完成以下三项任务:
1、给定y,z,p,计算Y^Z Mod P 的值;
2、给定y,z,p,计算满足xy≡ Z ( mod P )的最小非负整数;
3、给定y,z,p,计算满足Y^x ≡ Z ( mod P)的最小非负整数。
Input
输入包含多组数据。
第一行包含两个正整数T,K分别表示数据组数和询问类型(对于一个测试点内的所有数据,询问类型相同)。
以下行每行包含三个正整数y,z,p,描述一个询问。
Output
对于每个询问,输出一行答案。对于询问类型2和3,如果不存在满足条件的,则输出“Orz, I cannot find x!”,注意逗号与“I”之间有一个空格。
Sample Input
【样例输入1】
3 1
2 1 3
2 2 3
2 3 3
【样例输入2】
3 2
2 1 3
2 2 3
2 3 3
【数据规模和约定】
对于100%的数据,1<=y,z,p<=10^9,为质数,1<=T<=10。
Sample Output
【样例输出1】
2
1
2
【样例输出2】
2
1
0
HINT
Source
第一轮day1

/*
复合题hhh.
前两问裸的快速幂 exgcd.
读入int64 不要图快用scanf linux好像不行? 
然后这题case 3 是BSGS.
由于本人比较弱
所以我只能感性的认识一下BSGS.
y^x≡z(mod p).
这题暴力的话复杂度是O(p)的.
因为根据费马小定理y^(p-1)≡1(mod p).
剩余系元素的个数就是p-1,再往后就出现循环了.
然后我们采用分块的思想,令m=√p.
然后就有y^(km+i)≡z(mod p).
y^i≡ine(y^km)*z(mod p) (ine x为x的逆元).
然后左边hash存表,右边枚举k.
逆元是积性函数.
右边就变成了ine(y^m(k-x))*[ine(y^m)]^x.
枚举k即可.
然后因为费马小定理有y^m*y^(p-m-1)≡1(mod p).
so ine(y^m)=y^(p-m-1).
复杂度就变成了O(sqrt(p)).
*/
#include<iostream>
#include<cstdio>
#include<cmath>
#include<algorithm>
#include<map>
#define LL long long
using namespace std;
int k,t,n,p;
LL a,b,x,y,c;
map<int ,int >s;
LL mi()
{
    LL tot=1;
    while(b)
    {
        if(b&1) tot=tot*a%p;
        a=a*a%p;
        b>>=1;
    }
    return tot%p;
}
void slove1()
{
    while(t--)
    {
        cin>>a>>b>>p;// 1 W.
        //scanf("%lld%lld%lld",&a,&b,&p);
        printf("%lld\n",mi());
    }
}
void exgcd(LL a,LL b,LL &x,LL &y)
{
    if(!b) {x=1,y=0;return;}
    else exgcd(b,a%b,y,x),y-=(a/b)*x;
}
void slove2()
{
    while(t--)
    {
        cin>>a>>c>>b;
        LL g=__gcd(a,b);
        if(c%g) printf("Orz, I cannot find x!\n");
        else
        {
            x=y=0;
            exgcd(a,b,x,y);
            x*=c;g=b/g;
            x=(x%g+g)%g;
            cout<<x<<endl;
        }
    }
}
LL mi1(LL aa,LL bb,LL pp)
{
    LL tot=1;
    while(bb)
    {
        if(bb&1) tot=tot*aa%pp;
        aa=aa*aa%pp;
        bb>>=1;
    }
    return tot%pp;
}
 map<int,int> mp;
void slove3()
{
    int ans=0,y,z;
    bool flag=false;
    while(t--)
    {
        scanf("%d%d%d",&y,&z,&p);y%=p;
        if (!y&&!z) {printf("1\n");continue;}
        if (!y){printf("Orz, I cannot find x!\n");continue;}
        s.clear();
        LL m=ceil(sqrt(p)),tot=1;s[1]=m+1;
        for(int i=1;i<=m-1;i++)
        {
            tot=tot*y%p;
            if(!s[tot]) s[tot]=i;
        }
        bool flag=false;
        LL tmp=mi1(y,p-1-m,p),ni=1;//2 W.
        for(int k=0;k<=m-1;k++)
        {
            int i=s[z*ni%p];
            if(i)
            {
                if(i==m+1) i=0;
                flag=true;printf("%d\n",k*m+i);break;
            }
            ni=ni*tmp%p;
        }
        if(!flag) printf("Orz, I cannot find x!\n");
    }
    return ;
}
int main()
{

    scanf("%d%d",&t,&k);
    if(k==1) slove1();
    else if(k==2) slove2();
    else slove3();
    return 0;
}
posted @ 2017-02-06 07:24  nancheng58  阅读(146)  评论(0编辑  收藏  举报