Codeforces Round #485 (Div. 1) F Oppa Funcan Style Remastered

1 篇文章 0 订阅
1 篇文章 0 订阅

题目大意

N N 个舞台,每个舞台有一个fi,每秒钟i舞台上的人会走向舞台 fi f i ,给出 T T 组询问,现在时间为K秒钟, N N 个舞台,问你能不能构造出每个fi使得每个舞台上的人经过K秒钟,能回到自己原本的舞台上。

N<=1018,K<=1015,T<=104 N <= 10 18 , K <= 10 15 , T <= 10 4 不同的 K K 最多只有50

Analysis

若构造出一个环,使得 i i 舞台的人经过K秒后回到自己舞台上,那么该环经过的点都可以回到自己舞台上。而对于询问只用回答 YES Y E S NO N O ,环就可以任意构造。那么也就是拿 K K 的因子来填满长度为N的序列,考虑将 K K 分解质因数,那么也就是要求

iaipi=N
考虑质因子个数为 1 1 的时候怎么做,只需判断N是否能被 K K 整除。那么个数为2的时候呢,那也就是求解一个方程:

ax+by=N a x + b y = N
但这个方程有一些特殊, x x y互质,那么我们可以不使用 exgcd e x g c d 求解。对于方程两边对 x x 取模,可得
byN(modx)
那么
bNy1(modx) b ≡ N ∗ y − 1 ( m o d x )
求出 b b 后就可以判断一下by是否 <=N <= N <script type="math/tex" id="MathJax-Element-118"><=N</script>,就可以知道 ax a x 有没有整数解。那么对于质因子大于等于 3 3 的时候怎么做呢,我们发现,此时K最小的质因子不超过 105 10 5 ,且不同的 K K 比较少,考虑用图论解决。将K所有质因子从小到大排序,对于所有的 i<p1 i < p 1 ,做以下两边,枚举质因子 pj(j!=1) p j ( j ! = 1 ) i i (i+pj)modp1连边,然后跑一遍最短路,最后查一下 dis[N d i s [ N % p1] p 1 ] 是否 <=N <= N <script type="math/tex" id="MathJax-Element-131"><=N</script>,就可以知道是否有解了。因为这样构图,就是除第一个若干个质因子互相组合,最后判断是否能够加上一些第一个质因子来构造。但若这样互相组合出来已经大于 N N ,则无法构造。ps:可以不用 Pollard P o l l a r d _ rho r h o ,直接暴力分解。

代码

# include<cstdio>
# include<cstring>
# include<algorithm>
# include<cmath>
# include<queue>
# include<ctime>
using namespace std;
typedef long long ll;
const int N = 2e4 + 5;
const int M = 1e6 + 5;
struct node
{
    ll n,k;
    int pos;
}q[N];
struct data
{
    int id; ll d;
    bool operator < (data r) const
    { return d > r.d; }
};
priority_queue <data> g;
ll dis[M];
int ans[N],vis[M],p[M];
int tot,cnt;
bool cmp(node a,node b) { return a.k < b.k; }
ll abs1(ll x) { return x < 0 ? -x : x; }
ll gcd(ll x,ll y) { return !y ? x : gcd(y,x % y); }
inline ll mul(ll x,ll p,ll mo)
{
    ll ret = 0;
    for (; p ; p >>= 1,x = (x + x) % mo)
        if (p & 1) ret = (ret + x) % mo;
    return ret;
}
inline ll pow(ll x,ll p,ll mo)
{
    ll ret = 1;
    for (; p ; p >>= 1,x = mul(x,x,mo))
        if (p & 1) ret = mul(ret,x,mo);
    return ret;
}
inline bool Mill(ll x)
{
    if (x < 2) return 0;
    int s = 0; ll z = x; --x;
    while (!(x & 1)) x >>= 1,++s;
    for (int i = 0 ; i < 10 ; ++i)
    {
        ll k = (rand() * rand()) % z;
        while (!k) k = (rand() * rand()) % z;
        k = pow(k,x,z);
        ll pre = k;
        for (int j = 0 ; j < s ; ++j)
        {
            k = mul(k,k,z);
            if (k == 1 && pre != 1 && pre != z - 1) return 0;
            pre = k;
        }
        if (k != 1) return 0;
    }
    return 1;
}
inline ll rho(ll x)
{
    int l = 1,r = 2;
    ll x0 = (rand() * rand()) % x,y = x0,c = (rand() * rand()) % x + 1;
    while (1)
    {
        ++l;
        x0 = (mul(x0,x0,x) + c) % x;
        ll now = gcd(abs1(x0 - y),x);
        if (now != 1 && now != x) return now;
        if (x0 == y) return x;
        if (l == r) r <<= 1,y = x0;
    }
}
inline void divi(ll x)
{
    if (x == 1) return;
    if (Mill(x)) { p[++cnt] = x; return; }
    ll d = rho(x);
    divi(x / d); divi(d);
}
inline void dij()
{
    dis[0] = 0,g.push((data){0,0});
    while (!g.empty())
    {
        data x = g.top(); g.pop();
        if (vis[x.id]) continue;
        vis[x.id] = 1;
        for (int i = 2 ; i <= cnt ; ++i)
        {
            int v = (x.id + p[i]) % p[1];
            if (dis[v] > dis[x.id] + p[i])
            {
                dis[v] = dis[x.id] + p[i];
                g.push((data){v,dis[v]});
            }
        }
    }
}
int main()
{
    int t; scanf("%d",&t);
    for (int i = 1 ; i <= t ; ++i) scanf("%I64d%I64d",&q[i].n,&q[i].k),q[i].pos = i;
    sort(q + 1,q + t + 1,cmp);
    for (int i = 1 ; i <= t ; ++i)
    {
        if (q[i].k == q[i - 1].k)
        {
            if (!cnt) { ans[q[i].pos] = 0; continue; }
            if (cnt == 1)
            {
                if (q[i].n % q[i].k == 0) ans[q[i].pos] = 1;
                else ans[q[i].pos] = 0;
                continue;
            }
            if (cnt == 2)
            {
                ll b = q[i].n % p[1] * pow((ll)p[2],p[1] - 2,(ll)p[1]) % p[1];
                if (b * p[2] <= q[i].n) ans[q[i].pos] = 1;
                else ans[q[i].pos] = 0;
                continue;
            }
            ans[q[i].pos] = dis[q[i].n % (ll)p[1]] <= q[i].n;
            continue;
        }
        cnt = 0;
        divi(q[i].k);
        sort(p + 1,p + cnt + 1); cnt = unique(p + 1,p + cnt + 1) - p - 1;
        if (!cnt) { ans[q[i].pos] = 0; continue; }
        if (cnt == 1)
        {
            if (q[i].n % q[i].k == 0) ans[q[i].pos] = 1;
            else ans[q[i].pos] = 0;
            continue;
        }
        if (cnt == 2)
        {
            ll b = q[i].n % p[1] * pow((ll)p[2],p[1] - 2,(ll)p[1]) % p[1];
            if (b * p[2] <= q[i].n) ans[q[i].pos] = 1;
            else ans[q[i].pos] = 0;
            continue;
        }
        for (int j = 0 ; j < p[1] ; ++j) vis[j] = 0,dis[j] = 1e18;
        dij();
        ans[q[i].pos] = dis[q[i].n % p[1]] <= q[i].n;
    }
    for (int i = 1 ; i <= t ; ++i)
    if (ans[i]) puts("YES");
    else puts("NO");
    return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值