LG-P5285 [十二省联考 2019] 骗分过样例 题解

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

更好的阅读体验戳此进入

(建议您从上方链接进入我的个人网站查看此 Blog,在 Luogu 中图片会被墙掉,部分 Markdown 也会失效)


十分奇怪的一道题,不过其中涉及到的很多知识点却又是很实用的。

题意描述的已经很清楚了,这里对于每类点分别写一个 namespace 并分别讲解。

Default

这些是本题很多地方需要用到的一些函数,这里我把它们封装到一起:

namespace Default{
    mt19937 rnd(random_device{}());
    int rndd(int l, int r){return rnd() % (r - l + 1) + l;}
    template<typename T = int>
    inline T read(void){
        T ret(0);
        short flag(1);
        char c = getchar();
        while(c != '-' && !isdigit(c))c = getchar();
        if(c == '-')flag = -1, c = getchar();
        while(isdigit(c)){
            ret *= 10;
            ret += int(c - '0');
            c = getchar();
        }
        ret *= flag;
        return ret;
    }
    ll readBignum(ll MOD){
        ll ret(0);
        char c = getchar();
        while(!isdigit(c))c = getchar();
        while(isdigit(c)){
            ret = (ret * 10 + int(c - '0')) % MOD;
            c = getchar();
        }
        return ret;
    }
    ll readBignum(ll MOD, string num){
        ll ret(0);
        for(auto c : num){
            if(!isdigit(c))continue;
            ret = (ret * 10 + int(c - '0')) % MOD;
        }
        return ret;
    }
    ll qmul(ll x, ll y, ll MOD){
        return (__int128_t)x * y % MOD;
        // ll quot = (ld)x / MOD * y;
        // ll ret = (unll)x * y - (unll)quot * MOD;
        // return (ret + MOD) % MOD;
    }
    ll qpow(ll a, ll b, ll MOD){
        ll ret(1), mul(a);
        while(b){
            if(b & 1)ret = qmul(ret, mul, MOD);
            b >>= 1;
            mul = qmul(mul, mul, MOD);
        }
        return ret;
    }
    bool MillerRabin(ll n, bool special = false){
        int prime[20] = {0, 2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37};
        if(n % 2 == 0 || n <= 2)return n == 2;
        ll power(n - 1); int cnt(0);
        while(power % 2 == 0)power /= 2, ++cnt;
        int times = special ? 1 : 12;
        while(times--){
            if(n == prime[times + 1])return true;
            ll base = prime[times + 1], res = qpow(base, power, n);
            ll nxt;
            // if(res == n - 1 || res == 1)continue;
            for(int i = 1; i <= cnt; ++i){
                nxt = qmul(res, res, n);
                if(nxt == 1 && res != 1 && res != n - 1)return false;
                res = nxt;
            }
            if(nxt != 1)return false;
        }
        return true;
    }
}

下面对这些函数的作用进行简单说明:

mt19937 rnd(random_device{}());
int rndd(int l, int r);

随机数生成器。

template<typename T>
inline T read(void);

标准的快读模板。

ll readBignum(ll MOD);
ll readBignum(ll MOD, string num);

读入一些特别长的数,并在读入过程中取模。

ll qpow(ll a, ll b, ll MOD);

快速幂模板。

ll smul(ll x, ll y, ll MOD);

快速乘模板,用 __int128_t 实现。

bool MillerRabin(ll n, bool special = false);

用于快速判定是否为素数,关于 Miller Rabin

Case 1~3

简单观察发现输入为 $ 0, 1, 2, 3, \cdots $。

输出为 $ 1, 19, 361, 6859, \cdots $。

显然为 $ 19^0, 19^1, 19^2, 19^3, \cdots $。

所以显然这个功能即为输入 $ x $ 输出 $ 19^x $。

然后观察文件名发现其中有 998244353 字段,十分显然就是对其取模,即求的是 $ 19^x \bmod{998244353} $。

对于前两个点简单的快速幂就能过,而对于第三个点发现 $ x $ 很大,所以需要用到欧拉定理:

若 $ p $ 为素数,则有 $ a^{p - 1} \equiv 1 \pmod{p} $。

简单转化一下,令 $ 19^x \bmod{998244353} = \xi $ 则有:

\[\begin{aligned} &19^{p - 1} \equiv 1 \pmod{p} \\ \iff& 19^x \equiv 19^{x \bmod{p - 1}} \pmod{p} \\ \iff& \xi \equiv 19^{x \bmod{p - 1}} \pmod{p} \end{aligned} \]

所以对于这三个点丢可以直接输出 $ 19^{x \bmod{p - 1}} \bmod{998244353} $。

同时发现 $ x $ 超过 long long 了,于是我们需要在读入的时候边读入边取模。

namespace Case_1_2_3{
    void Make(ll MOD = 998244353){
        int N = Default::read();
        for(int i = 1; i <= N; ++i){
            ll x = Default::readBignum(MOD - 1);
            printf("%lld\n", Default::qpow(19, x, MOD));
        }
    }
}

Case 4

观察文件名发现,区别只是模数变成了 ?,于是这也很显然,我们需要确定模数。

观察样例,我们发现以下几个性质:

  • 枚举的下界要比答案中所有数大,即 $ 1145099 + 1 $,这个数可以手动找也可以写个程序,把输入定向到这个文件,然后找一下最大值,当然这个性质不考虑的话,嗯枚举似乎也不会耗费太久的时间(可能吧?
  • 虽然题里没说,但是我们可以假定这个模数一定为质数要不然就太毒瘤了
  • 对于判断一个数是否为质数可以直接用 MillerRabin。
  • 判断找的模数对不对可以考虑直接用第一个大数验证一下即可,也就是 $ 627811703016764290815178977207148434322 \Longrightarrow 642666 $。

于是写出枚举找素数的程序:

namespace Case_4{
    ll FindMod(void){
        string val_s("627811703016764290815178977207148434322");
        ll std_res = 642666;
        for(ll i = 1145099 + 1; i <= LLONG_MAX; ++i){
            if(i % 2 == 0 || !Default::MillerRabin(i))continue;
            ll val = Default::readBignum(i - 1, val_s);
            ll res = Default::qpow(19, val, i);
            if(res == std_res)return i;
        }
    }
    void Make(void){
        Case_1_2_3::Make(1145141ll);
    }
}

跑一下就可以得到素数为 $ 1145141 $。

Case 5

既然是升级版肯定不会让你这么简单枚举出来模数的

有一个很人类智慧的枚举方法,对于整个输入输出,我们要找到一对最接近的 $ x, y \quad x \lt y $,且对于答案有 $ ans_x > ans_y $。

这时简单想一下就会发现可以通过 $ x $ 的答案推出来没有取模完全的 $ y $ 的答案,也就是:$ ans_x \times 19^{y - x} \equiv ans_y \pmod{p} $。

或者写成:$ ans_x \times 19^{y - x} \bmod{p} = ans_y $。

所以显然这个时候我们需要枚举的模数就只可能是 $ ans_x \times 19^{y - x} - ans_y $ 的因数。

思路大概这样,实现就不写了,只需要注意两个点:

  • 首先为了这道题比较可做,模数应该会在 long long 范围内,我们也只需要在此范围内枚举。
  • 然后和之前一样,我们是存在枚举下界的,就是答案中最大的数加一,可以在跑 $ x, y $ 的时候顺便把下界跑出来。

总之最后得出来的结果应该是:

模数是 $ 719200885258981741674 $ 的因数,模数下界是 $ 5211500658258874319 $,最终计算得出模数为 $ 5211600617818708273 $。

namespace Case_5{
    void Make(void){
        Case_1_2_3::Make(5211600617818708273ll);
    }
}

Case 6~7

观察文件名发现序号没变,那么求的东西不变,然后观察文件名里有 wa,输出的答案里有负数,又联想到提示里的内容,所以显然这个是因为 int 溢出了所以变成负数。

然后我们感性理解一下,模意义下的很多模数都是一个群,从群的角度感性理解一下这个溢出应该也会有周期性,所以我们可以通过 map < int, int > 来找第一个重复出现的数,也就是周期。

大概的实现是这样:

namespace Case_6_7{
    const int MOD(998244353);
    int ans[11000000];
    int beg(-1), siz(-1);
    void FindCirc(void){
        map < int, int > mp;
        int base(1);
        mp.insert({ans[0] = base, 0});
        for(int i = 1; true; ++i){
            if(mp.find(base = signed((unsigned)base * (unsigned)19) % MOD) != mp.end()){beg = mp[base], siz = i - mp[base]; return;}
            else mp.insert({ans[i] = base, i});
        }
    }
    void Make(void){
        FindCirc();
        int N = Default::read();
        for(int i = 1; i <= N; ++i){
            ll x = Default::read<ll>();
            if(x < beg)printf("%d\n", ans[x]);
            else printf("%d\n", ans[(x - beg) % siz + beg]);
        }
    }
}

Case 8~10

序号变为 $ 2 $,所以显然要实现的功能改变了,观察文件名 p 显然代表着 Prime,简单看一下样例就能明白要实现的是对于 $ \left[l, r\right] $ 的数,质数输出 p,合数输出 .,简单用 Miller-Rabin 判一下即可。

namespace Case_8_9_10{
    void Make(void){
        int N = Default::read();
        for(int i = 1; i <= N; ++i){
            ll l = Default::read<ll>(), r = Default::read<ll>();
            for(ll x = l; x <= r; ++x){
                printf("%c", Default::MillerRabin(x) ? 'p' : '.');
            }
            printf("\n");
        }
    }
}

Case 11~13

观察文件名 u 显然是求莫比乌斯函数,对于第一个点显然打个线性筛就行,具体看这里

第二第三个点可以理解为求任意区间内的莫比乌斯函数,可以考虑把值域分一下,预处理出 $ (10{18}){3}} = 10^6 $ 以内的莫比乌斯函数和质数,然后在要求的区间内筛一下,把每个数中的 $ 10^6 $ 以内的质因子全部除下去,然后在除的时候注意维护莫比乌斯函数,特判一下如果有平方因子变成零就行,相信你们都会

然后显然对于区间内最后剩下的数一定是一下几个情况:

  • 本身就是一个质数,可以用 Miller-Rabin 判定,此时 $ \mu = -1 $。
  • 是某个大于 $ 10^6 $ 质数的完全平方,此时 $ \mu = 0 $。
  • 是两个大于 $ 10^6 $ 不同质数的乘积,此时 $ \mu = 1 $。

这里如果 $ \mu $ 已经为 $ 0 $ 或剩下的数已经为 $ 1 $ 了则可以不用讨论,是个小剪枝。

不过这题 Luogu 上卡常很离谱, $ 12, 13 $ 很难卡过,有一个很不严谨的卡常,通过面向数据剪枝我们可以发现如果把求莫比乌斯函数时候的 Miller-Rabin 测试次数设置为 $ 1 $,只测试底数为 $ 2 $,刚好可以把数据卡过不影响正确性,于是可以卡常过 Luogu,在 LOJ 上评测机比较快可以不用考虑这一点。

namespace Case_11_12_13{
    #define PRE (ll)(1e6)
    #define RPOS beg - l + 1
    void Make(void){
        vector < int > prime;
        bool vis[PRE + 10];
        int mu[PRE + 10];
        vis[1] = true, mu[1] = 1;
        for(int i = 2; i <= PRE; ++i){
            if(!vis[i]){
                vis[i] = true;
                mu[i] = -1;
                prime.push_back(i);
            }
            for(auto p : prime){
                if(p * i > PRE)break;
                vis[p * i] = true;
                mu[p * i] = i % p == 0 ? 0 : -mu[i];
                if(i % p == 0)break;
            }
        }
        int N = Default::read();
        while(N--){
            ll l = Default::read<ll>(), r = Default::read<ll>();
            if(r <= PRE){
                for(int i = l; i <= r; ++i){
                    printf("%c", mu[i] == 1 ? '+' : (mu[i] == -1 ? '-' : '0'));
                }
                printf("\n");
                continue;
            }
            int siz = r - l + 1;
            ll value[1100000];
            for(int i = 1; i <= siz; ++i)value[i] = (ll)i + l - 1;
            int mu_r[1100000];
            memset(mu_r, 0x3f, sizeof(mu_r));
            for(auto p : prime){
                ll times = l / p;
                ll beg = p * times;
                while(beg < l)beg += p;
                while(beg <= r){
                    value[RPOS] /= p;
                    if(value[RPOS] % p == 0)mu_r[RPOS] = 0;
                    mu_r[RPOS] = mu_r[RPOS] > 1 ? -1 : -mu_r[RPOS];
                    beg += p;
                }
            }
            for(int i = 1; i <= siz; ++i){
                if(mu_r[i] == 0 || value[i] == 1)continue;
                if(Default::MillerRabin(value[i], true))mu_r[i] = mu_r[i] > 1 ? -1 : -mu_r[i];
                else if(value[i] != 1 && (ll)sqrt(value[i]) * (ll)sqrt(value[i]) == value[i])mu_r[i] = 0;
                else if(mu_r[i] > 1)mu_r[i] = 1;
            }
            for(int i = 1; i <= siz; ++i)printf("%c", mu_r[i] == 1 ? '+' : (mu_r[i] == -1 ? '-' : mu_r[i] == 0 ? '0' : '?'));
            printf("\n");
        }
    }
}

Case 14~16

观察文件名 g 显然是要求区间内的数是否为原根。对于第一个点模数全为 $ 998244353 $,区间不是很大,直接把 $ \phi(998244353) $ 质因数分解然后对于区间内每一个数跑一遍暴力验证即可。即:

对于其所有质因数 $ frac(i) $ 有 $ g^{\frac{\phi(MOD)}{frac(i)}} \neq 1 $。

对于第二个点多了一个 $ 13123111 $,且区间较大,不能暴力跑,于是考虑有如下性质:

对于原根 $ g $, $ g^x \pmod{MOD} $ 为原根当且仅当 $ x \perp \phi(MOD) $,这个 $ x $ 似乎叫做指标。

可以考虑把其分解质因数,标记所有其质因数的区间内的倍数,也就是筛一下,最终所有没有标记的数可以表示为 $ g^x $ 的即为原根。

然后对于最后一个点,和之前的思路一样,显然模数是质数,把模数枚举一下,然后选二十个左右的数据,随便选一个质因子验证一下,很快就能跑出来模数为 $ 1515343657 $。

namespace Case_14_15_16{
    #define PRE (int)(1e6)
    vector < ll > prime;
    bool vis[PRE + 100];
    void Pretreat(void){
        vis[1] = true;
        for(int i = 2; i <= PRE; ++i){
            if(!vis[i])prime.push_back(i), vis[i] = true;
            for(auto p : prime){
                if(p * i > PRE)break;
                vis[p * i] = true;
                if(i % p == 0)break;
            }
        }
    }
    vector < ll > Resolve(ll x){
        #ifndef PRETREAT
        #define PRETREAT
        Pretreat();
        #endif//PRETREAT
        vector < ll > ret;
        for(auto p : prime){
            if(p * p > x)break;
            if(x % p == 0)ret.push_back(p);
            while(x % p == 0)x /= p;
        }
        if(x != 1)ret.push_back(x);
        return ret;
    }
    map < ll, ll > mp;
    ll FindMinG(ll x){
        if(mp.find(x) != mp.end())return mp[x];
        vector < ll > fact = Resolve(x - 1);
        for(ll i = 2; i <= (ll)pow(x, 0.25); ++i){
            bool flag(true);
            for(auto p : fact){
                if(Default::qpow(i, (x - 1) / p, x) == 1){flag = false; break;}
            }
            if(flag){
                mp.insert({x, i});
                return i;
            }
        }
        return -1;
    }
    void Make(void){
        int N = Default::read();
        for(int i = 1; i <= N; ++i){
            ll l = Default::read<ll>(), r = Default::read<ll>();
            ll MOD = l != 233333333ll ? Default::read<ll>() : 1515343657ll;
            if(MOD == 998244353ll || MOD == 1515343657ll){
                vector < ll > frac = Resolve(MOD - 1);
                for(ll x = l; x <= r; ++x){
                    bool flag(true);
                    for(auto p : frac)
                        if(Default::qpow(x, (MOD - 1) / p, MOD) == 1){printf("."); flag = false; break;}
                    if(flag)printf("g");
                }
                printf("\n");
                continue;
            }
            vector < ll > frac = Resolve(MOD - 1);
            ll G = FindMinG(MOD);//fprintf(stderr, "G: %lld\n", G);
            #define CMOD (13123111)
            bool mark[CMOD + 100];
            for(auto p : frac)
                for(ll j = 1; j * p <= CMOD; ++j)
                    mark[j * p] = true;
            int logg[CMOD + 100];
            memset(logg, 0, sizeof(logg));
            for(ll cur(1), base(0); base <= CMOD; ++base, cur = cur * G % CMOD)
                logg[cur] = base;
            for(ll x = l; x <= r; ++x)
                printf("%c", mark[logg[x]] ? '.' : 'g');
            printf("\n");
        }
    }
}

AC Code

#define _USE_MATH_DEFINES
#include <bits/stdc++.h>

#define PI M_PI
#define E M_E
#define npt nullptr
#define SON i->to

using namespace std;

mt19937 rnd(random_device{}());
int rndd(int l, int r){return rnd() % (r - l + 1) + l;}

typedef unsigned int uint;
typedef unsigned long long unll;
typedef long long ll;
typedef long double ld;

namespace Default{
    mt19937 rnd(random_device{}());
    int rndd(int l, int r){return rnd() % (r - l + 1) + l;}
    template<typename T = int>
    inline T read(void){
        T ret(0);
        short flag(1);
        char c = getchar();
        while(c != '-' && !isdigit(c))c = getchar();
        if(c == '-')flag = -1, c = getchar();
        while(isdigit(c)){
            ret *= 10;
            ret += int(c - '0');
            c = getchar();
        }
        ret *= flag;
        return ret;
    }
    ll readBignum(ll MOD){
        ll ret(0);
        char c = getchar();
        while(!isdigit(c))c = getchar();
        while(isdigit(c)){
            ret = (ret * 10 + int(c - '0')) % MOD;
            c = getchar();
        }
        return ret;
    }
    ll readBignum(ll MOD, string num){
        ll ret(0);
        for(auto c : num){
            if(!isdigit(c))continue;
            ret = (ret * 10 + int(c - '0')) % MOD;
        }
        return ret;
    }
    ll qmul(ll x, ll y, ll MOD){
        return (__int128_t)x * y % MOD;
        // ll quot = (ld)x / MOD * y;
        // ll ret = (unll)x * y - (unll)quot * MOD;
        // return (ret + MOD) % MOD;
    }
    ll qpow(ll a, ll b, ll MOD){
        ll ret(1), mul(a);
        while(b){
            if(b & 1)ret = qmul(ret, mul, MOD);
            b >>= 1;
            mul = qmul(mul, mul, MOD);
        }
        return ret;
    }
    bool MillerRabin(ll n, bool special = false){
        int prime[20] = {0, 2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37};
        if(n % 2 == 0 || n <= 2)return n == 2;
        ll power(n - 1); int cnt(0);
        while(power % 2 == 0)power /= 2, ++cnt;
        int times = special ? 1 : 12;
        while(times--){
            if(n == prime[times + 1])return true;
            ll base = prime[times + 1], res = qpow(base, power, n);
            ll nxt;
            // if(res == n - 1 || res == 1)continue;
            for(int i = 1; i <= cnt; ++i){
                nxt = qmul(res, res, n);
                if(nxt == 1 && res != 1 && res != n - 1)return false;
                res = nxt;
            }
            if(nxt != 1)return false;
        }
        return true;
    }
}

namespace Case_1_2_3{
    void Make(ll MOD = 998244353){
        int N = Default::read();
        for(int i = 1; i <= N; ++i){
            ll x = Default::readBignum(MOD - 1);
            printf("%lld\n", Default::qpow(19, x, MOD));
        }
    }
}
namespace Case_4{
    ll FindMod(void){
        string val_s("627811703016764290815178977207148434322");
        ll std_res = 642666;
        for(ll i = 1145099 + 1; i <= LLONG_MAX; ++i){
            if(i % 2 == 0 || !Default::MillerRabin(i))continue;
            ll val = Default::readBignum(i - 1, val_s);
            ll res = Default::qpow(19, val, i);
            if(res == std_res)return i;
        }
    }
    void Make(void){
        Case_1_2_3::Make(1145141ll);
    }
}
namespace Case_5{
    void Make(void){
        Case_1_2_3::Make(5211600617818708273ll);
    }
}
namespace Case_6_7{
    const int MOD(998244353);
    int ans[11000000];
    int beg(-1), siz(-1);
    void FindCirc(void){
        map < int, int > mp;
        int base(1);
        mp.insert({ans[0] = base, 0});
        for(int i = 1; true; ++i){
            if(mp.find(base = signed((unsigned)base * (unsigned)19) % MOD) != mp.end()){beg = mp[base], siz = i - mp[base]; return;}
            else mp.insert({ans[i] = base, i});
        }
    }
    void Make(void){
        FindCirc();
        int N = Default::read();
        for(int i = 1; i <= N; ++i){
            ll x = Default::read<ll>();
            if(x < beg)printf("%d\n", ans[x]);
            else printf("%d\n", ans[(x - beg) % siz + beg]);
        }
    }
}
namespace Case_8_9_10{
    void Make(void){
        int N = Default::read();
        for(int i = 1; i <= N; ++i){
            ll l = Default::read<ll>(), r = Default::read<ll>();
            for(ll x = l; x <= r; ++x){
                printf("%c", Default::MillerRabin(x) ? 'p' : '.');
            }
            printf("\n");
        }
    }
}
namespace Case_11_12_13{
    #define PRE (ll)(1e6)
    #define RPOS beg - l + 1
    void Make(void){
        vector < int > prime;
        bool vis[PRE + 10];
        int mu[PRE + 10];
        vis[1] = true, mu[1] = 1;
        for(int i = 2; i <= PRE; ++i){
            if(!vis[i]){
                vis[i] = true;
                mu[i] = -1;
                prime.push_back(i);
            }
            for(auto p : prime){
                if(p * i > PRE)break;
                vis[p * i] = true;
                mu[p * i] = i % p == 0 ? 0 : -mu[i];
                if(i % p == 0)break;
            }
        }
        int N = Default::read();
        while(N--){
            ll l = Default::read<ll>(), r = Default::read<ll>();
            if(r <= PRE){
                for(int i = l; i <= r; ++i){
                    printf("%c", mu[i] == 1 ? '+' : (mu[i] == -1 ? '-' : '0'));
                }
                printf("\n");
                continue;
            }
            int siz = r - l + 1;
            ll value[1100000];
            for(int i = 1; i <= siz; ++i)value[i] = (ll)i + l - 1;
            int mu_r[1100000];
            memset(mu_r, 0x3f, sizeof(mu_r));
            for(auto p : prime){
                ll times = l / p;
                ll beg = p * times;
                while(beg < l)beg += p;
                while(beg <= r){
                    value[RPOS] /= p;
                    if(value[RPOS] % p == 0)mu_r[RPOS] = 0;
                    mu_r[RPOS] = mu_r[RPOS] > 1 ? -1 : -mu_r[RPOS];
                    beg += p;
                }
            }
            for(int i = 1; i <= siz; ++i){
                if(mu_r[i] == 0 || value[i] == 1)continue;
                if(Default::MillerRabin(value[i], true))mu_r[i] = mu_r[i] > 1 ? -1 : -mu_r[i];
                else if(value[i] != 1 && (ll)sqrt(value[i]) * (ll)sqrt(value[i]) == value[i])mu_r[i] = 0;
                else if(mu_r[i] > 1)mu_r[i] = 1;
            }
            for(int i = 1; i <= siz; ++i)printf("%c", mu_r[i] == 1 ? '+' : (mu_r[i] == -1 ? '-' : mu_r[i] == 0 ? '0' : '?'));
            printf("\n");
        }
    }
    #undef PRE
    #undef RPOS
}
namespace Case_14_15_16{
    #define PRE (int)(1e6)
    vector < ll > prime;
    bool vis[PRE + 100];
    void Pretreat(void){
        vis[1] = true;
        for(int i = 2; i <= PRE; ++i){
            if(!vis[i])prime.push_back(i), vis[i] = true;
            for(auto p : prime){
                if(p * i > PRE)break;
                vis[p * i] = true;
                if(i % p == 0)break;
            }
        }
    }
    vector < ll > Resolve(ll x){
        #ifndef PRETREAT
        #define PRETREAT
        Pretreat();
        #endif//PRETREAT
        vector < ll > ret;
        for(auto p : prime){
            if(p * p > x)break;
            if(x % p == 0)ret.push_back(p);
            while(x % p == 0)x /= p;
        }
        if(x != 1)ret.push_back(x);
        return ret;
    }
    map < ll, ll > mp;
    ll FindMinG(ll x){
        if(mp.find(x) != mp.end())return mp[x];
        vector < ll > fact = Resolve(x - 1);
        for(ll i = 2; i <= (ll)pow(x, 0.25); ++i){
            bool flag(true);
            for(auto p : fact){
                if(Default::qpow(i, (x - 1) / p, x) == 1){flag = false; break;}
            }
            if(flag){
                mp.insert({x, i});
                return i;
            }
        }
        return -1;
    }
    void Make(void){
        int N = Default::read();
        for(int i = 1; i <= N; ++i){
            ll l = Default::read<ll>(), r = Default::read<ll>();
            ll MOD = l != 233333333ll ? Default::read<ll>() : 1515343657ll;
            if(MOD == 998244353ll || MOD == 1515343657ll){
                vector < ll > frac = Resolve(MOD - 1);
                for(ll x = l; x <= r; ++x){
                    bool flag(true);
                    for(auto p : frac)
                        if(Default::qpow(x, (MOD - 1) / p, MOD) == 1){printf("."); flag = false; break;}
                    if(flag)printf("g");
                }
                printf("\n");
                continue;
            }
            vector < ll > frac = Resolve(MOD - 1);
            ll G = FindMinG(MOD);//fprintf(stderr, "G: %lld\n", G);
            #define CMOD (13123111)
            bool mark[CMOD + 100];
            for(auto p : frac)
                for(ll j = 1; j * p <= CMOD; ++j)
                    mark[j * p] = true;
            int logg[CMOD + 100];
            memset(logg, 0, sizeof(logg));
            for(ll cur(1), base(0); base <= CMOD; ++base, cur = cur * G % CMOD)
                logg[cur] = base;
            for(ll x = l; x <= r; ++x)
                printf("%c", mark[logg[x]] ? '.' : 'g');
            printf("\n");
        }
    }
}

int main(){
    // freopen("in.txt", "r", stdin);
    // freopen("out.txt", "w", stdout);
    string filename; cin >> filename;
    if(!filename.compare("1_998244353"))Case_1_2_3::Make();
    if(!filename.compare("1?"))Case_4::Make();
    if(!filename.compare("1?+"))Case_5::Make();
    if(!filename.compare("1wa_998244353"))Case_6_7::Make();
    if(!filename.compare("2p"))Case_8_9_10::Make();
    if(!filename.compare("2u"))Case_11_12_13::Make();
    if(!filename.compare("2g") || ! filename.compare("2g?"))Case_14_15_16::Make();

    fprintf(stderr, "Time: %.6lf\n", (double)clock() / CLOCKS_PER_SEC);
    return 0;
}

UPD

update-2022_09_20 初稿

posted @ 2022-09-20 11:01  Tsawke  阅读(25)  评论(0编辑  收藏  举报