Luogu P5285 [十二省联考2019]骗分过样例

Preface

ZJOI一轮被麻将劝退的老年选手看到这题就两眼放光,省选也有乱搞题

然后狂肝了3~4天终于打完了,期间还补了一堆姿势

由于我压缩技术比较菜,所以用的都是非打表算法,所以一共写了5K……

话不多说我们慢慢分析这道神题(真的是慢慢,最后还会放上许多辅助的CODE)


Case1~Case3

首先这几个点就是让你熟悉一下题目意思的

我们套路地发现这题由两部分组成,他们的功能编号开头分别为\(1/2\)

然后点开第一个点,发现第一个是数据组数?剩下的输入一个\(x\)然后就输出一个数

通过\(0\to1,1\to 19,2\to 361\)我们发现这个就是\(19^x\)

之后就能理解\(998244353\)是什么了,其实就是模数

那么很简单,Case1暴力推就可以了,Case2指数在long long内直接快速幂即可

Case3我们通过欧拉定理\(x^p=x^{p\mod\phi(p)}\),当\(\text{p is a prime number}\)时,有\(x^p=x^{p\mod p-1}\)

一边读入一边取模即可


Case4

\(998244353\)变成了?,我们发现这时候模数要自己猜

打开答案文件发现数据普遍很小,所以我们找出最大的数\(1145098\),然后向后枚举判断即可

判断的时候只需要比对第一个的答案即可,这个可以很快找出最后的模数是\(1145141\)


Case5

哎呀怎么又没有模数了,打开答案文件一看那庞大的数据范围看来就不能暴力枚举模数了

那么我们考虑我们一般找模数的过程,像这种单调增函数取模的时候肯定存在一个\(x\),使得\(f(x-1)>f(x)\)\(f(x)=19^x\mod p\)

那么我们考虑用这种方法求出模数,首先找出一对\(x,y(x<y)\)使得\(f(x)>f(y)\)\(x,y\)尽量接近

写个代码跑一下会得到这样的一组数据:

264708066 1996649514996338529
264708068 1589589654696467295

然后我们计算差值,\(264708066\times19^2-1589589654696467295=719200885258981741674\)

显然此时模数必定是\(719200885258981741674\)的一个因数,我们考虑它还要大于最大数\(5211500658258874318\),所以在\([100,200]\)中枚举一个约数,求出另一个约数\(5211600617818708273\)即是最后的模数

下面放一下找差值的代码以及求约数的Python代码:

#include<cstdio>
#include<cctype>
#include<algorithm>
using namespace std;
const int N=10005;
struct data
{
    int x; long long y;
    friend inline bool operator < (const data& A,const data& B)
    {
        return A.x<B.x;
    }
}a[N]; int n,mi=1e9,pos;
int main()
{
    freopen("mindlt.txt","w",stdout);
    FILE *fin1=fopen("software.in","r"),*fin2=fopen("software5.ans","r");
    register int i; char str[20]; fscanf(fin1,"%s",str+1);
    for (fscanf(fin1,"%d",&n),i=1;i<=n;++i) fscanf(fin1,"%d",&a[i].x);
    for (i=1;i<=n;++i) fscanf(fin2,"%lld",&a[i].y);
    for (sort(a+1,a+n+1),i=2;i<=n;++i) if (a[i].y<a[i-1].y)
    {
        if (a[i].x-a[i-1].x<mi) mi=a[i].x-a[i-1].x,pos=i;
    }
    return printf("%d %lld\n%d %lld",a[pos-1].x,a[pos-1].y,a[pos].x,a[pos].y),0;
}
for i in range(100,201):
    if 719200885258981741674%i==0:
        if 719200885258981741674//i>5211500658258874318:
            print(719200885258981741674//i,end=' ')
            

最后提醒一点,两个模数范围的数相加都会爆long long,所以必须开unsigned long long

Case6~Case7

艾玛这个\(998244353\)前面加个wa是什么鬼

打开答案文件发现输出有了负数,相信这个大家平时都有经历过,显然是没开long long炸了

那么快速幂我怎么知道要在哪里取模呢?没事Case6单次暴力乘起来即可,但是这样过不了Case7啊

不要慌我们意识到这种构造数列的方法类似于生日悖论,是很容易成环的

那么我们直接开一个map找出环然后直接做即可

这里给出找环的CODE,输出的第一个数是环之前的位置,第二个数是环的长度

#include<cstdio>
#include<map>
#define RI register int
using namespace std;
const int N=10000005,mod=998244353;
int lst,nw; map <int,int> pos;
int main()
{
    freopen("circle.txt","w",stdout);
    pos[lst=1]=0; for (RI i=1;;++i)
    {
        nw=19*lst%mod; if (pos.count(nw))
        return printf("%d %d",pos[nw]-1,i-pos[nw]),0;
        pos[nw]=i; lst=nw;
    }
    return 0;
}

Case8~Case10

woc终于写完第一个部分了,然后发现这第二个点的输出怎么都那么大呢

点开一看woc一片乱七八糟的字符?大致发现了输入可能是一个区间?然后每个数输出了一个字符

仔细分析加上适当联想(\(\text{p=prime}\))我们发现这就是个素性检测

那么很简单,Case8一发线性筛,Case9可以筛出前\(10^6\)然后拿到这里筛,Case10。。。

可以选择打表,因为两个素数间的间隔很大,可以直接记录差值然后压缩起来

但是判素数我们还有一大法宝——Miller_Rabin,这里的MR不需卡常以及鬼畜选参数,直接做\(10\)次都能过

所以还算是轻松地结束了


Case11~Case13

这里根据上面的套路我们点开答案文件发现只有0,+,-三种字符

然后结合\(u\)的提示很容易想到这就是区间求\(\mu\),根据\(\mu\)的定义我们大力来一发质因数分解?

然后悲惨的我复习了Pollard-Rho之后发现这东西在运行多组数据时就是个弟弟,连Case11都跑不过233

那只能沿袭上面的套路了,Case11可以线性筛,Case12直接用\(10^6\)的素数来筛即可

那么Case13的做法就比较诡异了,这里讲一种LOJ上看到的做法:

首先类似于Case12,先筛出\(10^6\)内所有素数,然后先像上面一样筛一遍,同时记录每一个数此时的乘积

分解结束后看一下,如果还有剩下的因数,那么其实只要判断以下情况即可:

  • 剩下的因数为素数,直接MR判一发即可
  • 剩下一个平方数,直接sqrt后回代判断,并且直接把\(\mu\)置为\(0\)
  • 这个数是两个不同的大于\(10^6\)的素数的乘积(容易发现不可能是三个及以上的积),这个什么都不干即可(乘两个\(-1\)等于不变)

然后写一发你就会发现TLE飞了,看一下真正慢的其实是MR哪里

然后我们就要利用欧洲玄学了,经过试验我们发现MR的底数可以只用\(2,3\)(据说只用一个\(2\)都能过)来跑,然后速度上卡过去了,且没有打表需求!


Case14~Case16

这个根据上面的套路直接观察+大力猜测发现\(原根\text{g=原根}\)

然后根据基本数论技巧我们知道判断一个数是否是另一个数的原根只要判断对于\(p\)的所有质因数\(p'\)都有\(x^{\frac{\phi(p)}{p'}}\mod p\not= 1\)

然后发现\(\phi(998244353)=998244352\)只有\(3\)个质因数,所以暴力做就可以通过Case14

那么Case15是什么鬼,区间筛原根?其实还真的可以,我们利用原根于指标的关系可以发现先找出任意一个原根,然后求出关于它的指标就得到这个数是原根当且仅当该指标与\(\phi(p)\)互质

这个直接大力分解质因数枚举一发即可,即可通过Case15

然后就是最恶心的Case16了,感觉上可以用偏数学的方法求出,但是我们可以乱搞求出模数

意识到\([10^9,2\times10^9]\)内很多素数,出题人为了卡你肯定要把正确的那个放在中间一点

所以我们从中间向两侧枚举,用MR判素数,然后怎么检测这个数是否符合要求呢

很简单,我们把答案的前\(50\)个全部记下来,因为\(\phi(p) \mod 2=0\),所以必然有质因数\(2\)

我们只用这个来判断,一旦出现这个都不合法但答案却是合法的情况即说明这个数显然不行

那么耐性地等待一小会(20s不到)我们就可以得到模数\(1515343657\)了(正着找可能要几分钟吧)

这里给出暴力找的CODE

#include<cstdio>
#include<cctype>
#define RI register int
#define CI const int&
using namespace std;
const int MR_prime[3]={2,3,61},ST=233333333,MID=1500000000;
const bool tar[50]={0,0,0,1,1,1,0,0,1,1,0,1,0,0,0,1,0,0,1,1,0,0,1,0,1,1,0,0,0,0,0,0,1,1,0,0,1,0,0,0,0,0,0,1,1,0,0,1,1,0};
inline int quick_pow(int x,int p,int mod,int mul=1)
{
    for (;p;p>>=1,x=1LL*x*x%mod) if (p&1) mul=1LL*mul*x%mod; return mul;
}
inline bool Singer_MR(CI x,CI p)
{
    if (quick_pow(x,p-1,p)!=1) return 0;
    int t=p-1; while (!(t&1))
    {
        t>>=1LL; int val=quick_pow(x,t,p);
        if (val!=1&&val!=p-1) return 0;
        if (val==p-1) return 1;
    }
    return 1;
}
inline bool Miller_Rabin(CI x)
{
    RI i; for (i=0;i<3;++i) { if (x==MR_prime[i]) return 1; if (x%MR_prime[i]==0) return 0; }
    for (i=0;i<3;++i) if (!Singer_MR(MR_prime[i],x)) return 0; return 1;
}
inline bool check(CI mod)
{
    for (RI i=0;i<50;++i) if (quick_pow(ST+i,mod-1>>1,mod)==1&&tar[i]) return 0; return 1;
}
int main()
{
    freopen("gr_mod.txt","w",stdout);
    for (RI i=0;;++i)
    {
        if (Miller_Rabin(MID-i)&&check(MID-i)) { printf("%d %d",MID-i,i); break; }
        if (Miller_Rabin(MID+i)&&check(MID+i)) { printf("%d %d",MID+i,i); break; }
    }
    return 0;
}

ALL CODE

#include<cstdio>
#include<cctype>
#include<cmath>
#include<cstring>
#define RI register int
#define RL register LL
#define CI const int&
#define CL const LL&
#define Tp template <typename T>
using namespace std;
typedef long long LL;
class FastInputOutput
{
    public:
        int pt[25];
        Tp inline void read(T& x)
        {
            x=0; char ch; while (!isdigit(ch=getchar()));
            while (x=(x<<3)+(x<<1)+(ch&15),isdigit(ch=getchar()));
        }
        Tp inline void read(T& x,const T& mod)
        {
            x=0; char ch; while (!isdigit(ch=getchar()));
            while (x=(10ull*x+(ch&15))%mod,isdigit(ch=getchar()));
        }
        Tp inline void write(T x)
        {
            if (!x) return (void)(putchar('0'),putchar('\n'));
            if (x<0) putchar('-'),x=-x;
            RI ptop=0; while (x) pt[++ptop]=x%10,x/=10;
            while (ptop) putchar(pt[ptop--]+48); putchar('\n');
        }
}F;
namespace Case1 //19^x%mod
{
    static const int L=101000,st=55244,len=45699;
    int n,val[L+5]; LL x,mod;
    inline void inc(LL& x,CL y)
    {
        unsigned long long t=x+y; x=t>=mod?t-mod:t;
    }
    inline LL quick_mul(LL x,LL y,LL sum=0)
    {
        for (;y;inc(x,x),y>>=1) if (y&1) inc(sum,x); return sum;
    }
    inline LL quick_pow(LL p,LL x=19,LL mul=1)
    {
        for (;p;p>>=1,x=quick_mul(x,x))
        if (p&1) mul=quick_mul(mul,x); return mul;
    }
    inline void work(void)
    {
        for (F.read(n);n;--n) F.read(x,mod-1),F.write(quick_pow(x));
    }
    inline void wa_work(void)
    {
        val[0]=1; for (RI i=1;i<=L;++i) val[i]=19*val[i-1]%mod;
        for (F.read(n);n;--n) F.read(x),F.write(val[x<=st?x:((x-st)%len+st)]);
    }
    /*inline int str_mod(const char *str,CI mod,int ret=0)
    {
        int len=strlen(str); for (RI i=0;i<len;++i)
        ret=((ret<<3)+(ret<<1)+(str[i]&15))%mod; return ret;
    }*/
    inline void solve(void)
    {
        char opt=getchar(); if (opt=='_') return F.read(n),mod=998244353,work();
        if (opt=='w') return F.read(n),mod=998244353,wa_work();
        /*for (RI i=1145099;;++i) if (Miller_Rabin(i))
        {
            if (mod=i,quick_pow(str_mod("627811703016764290815178977207148434322",mod-1))==642666)
            return;
        }*/
        opt=getchar(); if (opt=='+') mod=5211600617818708273LL; else mod=1145141; work();
    }
};
namespace Case2 //number identify
{
    const int MR_prime[3]={2,3},N=1e6,MX=13123111;
    int n,prime[N+5],id[MX+5],mpr[N+5],mu[N+5],cnt,tot,mod;
    LL l,r,mul[N+5],t; bool vis[MX+5];
    inline LL quick_mul(CL x,CL y,CL mod)
    {
        LL k=(LL)((1.0L*x*y)/(1.0L*mod)),r=x*y-k*mod;
        r-=mod; while(r<0LL) r+=mod; return r;
    }
    inline LL quick_pow(LL x,LL p,LL mod,LL mul=1)
    {
        for (;p;p>>=1,x=quick_mul(x,x,mod))
        if (p&1) mul=quick_mul(mul,x,mod); return mul;
    }
    inline bool Singer_MR(CI x,CL p)
    {
        if (quick_pow(x,p-1,p)!=1) return 0;
        LL t=p-1; while (!(t&1))
        {
            t>>=1LL; LL val=quick_pow(x,t,p);
            if (val!=1&&val!=p-1) return 0;
            if (val==p-1) return 1;
        }
        return 1;
    }
    inline bool Miller_Rabin(CL x)
    {
        RI i; for (i=0;i<2;++i) { if (x==MR_prime[i]) return 1; if (x%MR_prime[i]==0) return 0; }
        for (i=0;i<2;++i) if (!Singer_MR(MR_prime[i],x)) return 0; return 1;
    }
    inline void prime_solve(void)
    {
        for (F.read(n);n;--n)
        {
            F.read(l); F.read(r); for (RL i=l;i<=r;++i)
            putchar(Miller_Rabin(i)?'p':'.'); putchar('\n');
        }
    }
    #define Pi prime[j]
    inline void init(CI n)
    {
        vis[1]=mu[1]=1; for (RI i=2;i<=n;++i)
        {
            if (!vis[i]) prime[++cnt]=i,mu[i]=-1;
            for (RI j=1;j<=cnt&&i*Pi<=n;++j)
            {
                vis[i*Pi]=1; if (i%Pi) mu[i*Pi]=-mu[i]; else break;
            }
        }
    }
    inline void mu_solve(void)
    {
        for (init(N),F.read(n);n;--n)
        {
            RI i,j; F.read(l); F.read(r); int len=r-l+1; if (r<=N)
            {
                for (i=l;i<=r;++i) putchar(mu[i]?(~mu[i]?'+':'-'):'0');
                putchar('\n'); continue;
            }
            for (i=1;i<=len;++i) mu[i]=mul[i]=1; for (j=1;j<=cnt;++j)
            for (RL i=1LL*((l-1)/Pi+1)*Pi;i<=r;i+=Pi)
            if (i%(1LL*Pi*Pi)==0) mu[i-l+1]=0; else mu[i-l+1]*=-1,mul[i-l+1]*=Pi;
            for (i=1;i<=len;++i)
            {
                if (!mu[i]) { putchar('0'); continue; }
                if (mul[i]!=i+l-1)
                {
                    if (Miller_Rabin(t=(i+l-1)/mul[i])) mu[i]*=-1; else
                    {
                        LL sq=(LL)sqrt(t); if (sq*sq==t) mu[i]=0;
                    }
                }
                if (!mu[i]) putchar('0'); else putchar(~mu[i]?'+':'-');
            } putchar('\n');
        }
    }
    inline void resolve(int x)
    {
        tot=0; for (RI j=1;Pi*Pi<=x;++j) if (x%Pi==0)
        {
            mpr[++tot]=Pi; while (x%Pi==0) x/=Pi;
        }
        if (x>1) mpr[++tot]=x;
    }
    #undef Pi
    inline bool is_GR(CI x)
    {
        for (RI i=1;i<=tot;++i)
        if (quick_pow(x,(mod-1)/mpr[i],mod)==1) return 0; return 1;
    }
    inline void GR_Sieve(CI mod)
    {  
        RI i,j; memset(vis,0,sizeof(vis)); int gr,pfx;
        for (i=1;i<mod;++i) if (is_GR(i)) { gr=i; break; }
        for (i=1,pfx=gr;i<mod;++i,pfx=1LL*pfx*gr%mod) id[pfx]=i;
        for (i=1;i<=tot;++i) for (j=mpr[i];j<mod;j+=mpr[i]) vis[j]=1;
        for (i=1;i<mod;++i) putchar(vis[id[i]]?'.':'g');
    }
    inline void GR_solve(void)
    {
        for (init(1e5),F.read(n);n;--n)
        {
            F.read(l); F.read(r); if (r==234133333) mod=1515343657; else F.read(mod);
            if (mod==998244353||mod==1515343657)
            {
                resolve(mod-1); for (RI i=l;i<=r;++i)
                putchar(is_GR(i)?'g':'.');
            } else resolve(mod-1),GR_Sieve(mod); putchar('\n');
        }
    }
    inline void solve(void)
    {
        char opt=getchar(); switch (opt)
        {
            case 'p':
                prime_solve(); break;
            case 'u':
                mu_solve(); break;
            case 'g':
                GR_solve(); break;
        }
    }
};
int main()
{
    //freopen("software.in","r",stdin); freopen("software.out","w",stdout);
    char opt=getchar(); if (opt=='1') Case1::solve(); else Case2::solve();
    return 0;
}

Postscript

至此这道毒瘤题终于宣告终结,不得不说它改变了传统题的出题方式

放在赛场上这题就是防AK的绝佳手段,平时肝一肝可以,但是比赛的时候最好还是按照性价比拿分,合理安排吧

最后小声BB一句如果ZJOI也有这种乱搞题那不是很有趣(希望吉利二试能了一个心愿)233

posted @ 2019-04-14 20:46 hl666 阅读(...) 评论(...) 编辑 收藏