Codeforces 1325E 线性筛 + BFS 树

164 篇文章 0 订阅
题意

传送门 Codeforces 1325E Ehab’s REAL Number Theory Problem

题解

若任一元素存在为完全平方数的因子,则约去这些因子对答案没有影响;因为完全平方数除一个为完全平方数的因子,仍是完全平方数。由于任意元素至多存在 7 7 7 个因子,故约去完全平方数因子后,任意元素只可能为 1 , p , p q 1,p,pq 1,p,pq,其中 p , q p,q p,q 为素数。

将任意元素建模为边,即上述三种形式分别对应 1 − 1 , 1 − p , p − q 1-1,1-p,p-q 11,1p,pq。此时问题转化为取一组最小的边集,使边集的所有端点都经过偶数次;即求图中最小环。

边权为 1 1 1 的图上求最小环,可以通过构造 B F S BFS BFS 树求解(利用了 B F S BFS BFS 树的非树边只可能连向同层或相邻两层的性质)。枚举 B F S BFS BFS 树的根节点 r t rt rt,即枚举最小环所经过的节点,单次 B F S BFS BFS 求出经过 r t rt rt 的最小环的大小;具体而言,对于每条非树边 ( u , v ) (u,v) (u,v),用 d s [ u ] + d s [ v ] + 1 ds[u]+ds[v]+1 ds[u]+ds[v]+1 更新答案,其中 d s [ u ] ds[u] ds[u] 代表 u u u r t rt rt 的最短距离。

由于任意元素不大于 max ⁡ { a i } \max\{a_i\} max{ai},则任一边的一个端点必小于等于 max ⁡ { a i } \sqrt{\max\{a_i\}} max{ai} ,那么枚举这些端点即可。总时间复杂度 O ( max ⁡ { a i } ln ⁡ max ⁡ { a i } n ) O(\frac{\sqrt{\max\{a_i\}}}{\ln \sqrt{\max\{a_i\}}}n) O(lnmax{ai} max{ai} n)

#include <bits/stdc++.h>
using namespace std;
#define rep(i, l, r) for (int i = l, _ = r; i < _; ++i)
#define pb push_back
typedef long long ll;
const int MAXN = 1E5 + 5, MAXA = 1E6 + 5, INF = 0x3f3f3f3f;
int N, A[MAXN];
set<int> fac[MAXA];
int prime[MAXA], minp[MAXA], pn;
vector<int> G[MAXA];
int ds[MAXA], par[MAXA];

int bfs(int s)
{
    int res = INF;
    ds[1] = INF;
    rep(i, 0, pn) ds[prime[i]] = INF;
    queue<int> q;
    q.push(s);
    ds[s] = 0;
    while (q.size())
    {
        int u = q.front();
        q.pop();
        for (auto &v : G[u])
        {
            if (ds[v] == INF)
                ds[v] = ds[u] + 1, par[v] = u, q.push(v);
            else if (v != par[u])
                res = min(res, 1 + ds[u] + ds[v]);
        }
    }
    return res;
}

int main()
{
    ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
    cin >> N;
    rep(i, 0, N) cin >> A[i];
    rep(i, 2, MAXA)
    {
        if (!minp[i])
            prime[pn++] = i, minp[i] = i;
        for (int j = 0, k; j < pn && (k = prime[j] * i) < MAXA; ++j)
        {
            minp[k] = prime[j];
            if (minp[i] == prime[j])
                break;
        }
    }
    rep(i, 2, MAXA)
    {
        int p = minp[i];
        fac[i] = fac[i / p];
        if (fac[i].count(p))
            fac[i].erase(p);
        else
            fac[i].insert(p);
    }
    rep(i, 0, N)
    {
        int sz = fac[A[i]].size();
        int u, v;
        if (sz == 0)
            u = v = 1;
        else if (sz == 1)
            u = 1, v = *fac[A[i]].begin();
        else
            u = *fac[A[i]].begin(), v = *(++fac[A[i]].begin());
        G[u].pb(v), G[v].pb(u);
    }
    int res = bfs(1);
    rep(i, 0, pn)
    {
        int p = prime[i];
        if (p * p >= MAXA)
            break;
        res = min(res, bfs(p));
    }
    cout << (res == INF ? -1 : res) << '\n';
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值