51nod 1203 JZPLCM

本文介绍了一种利用质因数分解和RMQ算法解决特定数学问题的方法。通过将质因子分为小于和大于根号n两部分,并分别采用RMQ算法和莫队算法进行处理,最终实现了高效的时间复杂度。

原题链接.

据说这是某国家队爷的题,怪不得我一眼不会。

求lcm即求各个质因子对应得指数的最大值。

这时候想到以前做过几道类似的题怎么做的。

<n 的质因子分成一块,大于 >n 的质因子分成一块。

这样做的优势是 <n 的质因子的个数少,
而, >n 的质因子的指数最大为1。

那么这道题就显然了。

<n 的质因子强行RMQ,因为只有48个。

>n 的质因子因为指数小于等于1,可以直接上莫队,提前处理一下逆元就好了。

时间复杂度 O(48n log n+nn)

Code:

#include<cmath>
#include<cstdio>
#include<cstring>
#include<algorithm>
#define ll long long
#define fo(i, x, y) for(int i = x; i <= y; i ++)
#define min(a, b) ((a) < (b) ? (a) : (b))
#define max(a, b) ((a) > (b) ? (a) : (b))
using namespace std;

const int N = 50005, M = 223;

int bz[N], p[N], zp[N];


void Shai() {
    fo(i, 2, 5e4) {
        if(!bz[i]) p[++ p[0]] = i;
        fo(j, 1, p[0]) {
            if(i * p[j] > 5e4) break;
            bz[i * p[j]] = 1;
            if(i % p[j] == 0) break;
        }
    }
    int t = 0;
    fo(i, 2, 5e4) if(!bz[i])
        zp[i] = ++ t;
}

int num[N];

void Build() {
    int tot = 0;
    fo(i, 1, 5e4) {
        num[i] = num[i - 1];
        if((i - 1) % M == 0) num[i] ++;
    }
}

int n, Q, x, y, a[N], p0[N], u[N][20], v[N][20];

struct Ask {
    int l, r, i;
} c[N];

void fen(int a, int x) {
    p0[a] = 0;
    for(int i = 1; i <= p[0] && p[i] * p[i] <= x; i ++) {
        if(x % p[i] == 0) {
            u[a][++ p0[a]] = i;
            v[a][p0[a]] = 0;
            while(x % p[i] == 0)
                x /= p[i], v[a][p0[a]] ++;
        }
    }
    if(x > 1) {
        u[a][++ p0[a]] = zp[x];
        v[a][p0[a]] = 1;
    }
}

bool rank_c(Ask a, Ask b) {
    if(num[a.l] < num[b.l]) return 1;
    if(num[a.l] > num[b.l]) return 0;
    return a.r < b.r;
}


int f[16][N], a2[16];

int query(int i, int j) {
    int lg = log2(j - i + 1);
    return max(f[lg][i], f[lg][j - a2[lg] + 1]);
}

const int mo = 1e9 + 7;

ll ksm(ll x, ll y) {
    ll s = 1;
    for(; y; y >>= 1, x = x * x % mo)   
        if(y & 1) s = s * x % mo;
    return s;
}

ll ni[N], ans[N];

ll sum;

int t[N];

void add(int x, int y) {
    t[x] += y;
    if(t[x] == 0) sum = sum * ni[x] % mo;
    if(t[x] == 1 && y == 1) sum = sum * p[x] % mo;
}

void chan(int x) {
    int k = bz[x] ? -1 : 1; bz[x] = !bz[x];
    fo(i, 1, p0[x]) if(u[x][i] > 48)
        add(u[x][i], k);
}

int main() {
    Shai();
    Build();
    scanf("%d %d", &n, &Q);
    fo(i, 1, n) scanf("%d", &a[i]), fen(i, a[i]);
    fo(i, 1, Q) scanf("%d %d", &c[i].l, &c[i].r), c[i].i = i;
    fo(i, 1, Q) ans[i] = 1;
    a2[0] = 1; fo(i, 1, 15) a2[i] = a2[i - 1] * 2;
    fo(k, 1, 48) {
        fo(i, 1, n) f[0][i] = 0;
        fo(i, 1, n) fo(j, 1, p0[i])
            if(u[i][j] == k) f[0][i] = v[i][j];
        fo(j, 1, 15) fo(i, 1, n) f[j][i] = max(f[j - 1][i], f[j - 1][min(i + a2[j - 1], n)]);
        fo(i, 1, Q) ans[i] = ans[i] * ksm(p[k], query(c[i].l, c[i].r)) % mo;
    }
    sort(c + 1, c + Q + 1, rank_c);
    fo(i, 1, p[0]) ni[i] = ksm(p[i], mo - 2);
    memset(bz, 0, sizeof(bz));
    int x = 1, y = 0; sum = 1;
    fo(i, 1, Q) {
        while(x < c[i].l) chan(x ++);
        while(x > c[i].l) chan(-- x);
        while(y < c[i].r) chan(++ y);
        while(y > c[i].r) chan(y --);
        ans[c[i].i] = ans[c[i].i] * sum % mo;
    }
    fo(i, 1, Q) printf("%lld\n", ans[i]);
}
目前没有关于51nod 3478题目的具体描述和官方公布的C++解决方案代码。以下是一种通用的解题思路以及一个示例C++代码模板,可以用于解决类似的问题。 ### 问题解题思路 51nod 3478通常可能涉及以下算法或技术: - 动态规划(DP)或状态转移方程 - 贪心算法 - 数据结构(如线段树、堆、优先队列等) - 图论算法(如最短路径、最小生成树等) ### 示例C++代码模板 以下是一个通用的C++代码框架,适用于需要读取输入并处理大规模数据的问题: ```cpp #include <bits/stdc++.h> using namespace std; typedef long long ll; const int MAXN = 100005; // 根据题目规模调整 int n; ll k; ll a[MAXN]; int main() { ios::sync_with_stdio(false); cin.tie(0); cout.tie(0); cin >> n >> k; for (int i = 1; i <= n; ++i) { cin >> a[i]; a[i] += a[i - 1]; // 前缀和 } // 示例逻辑:查找是否存在和为k的连续子数组 unordered_map<ll, int> prefix_map; prefix_map[0] = 0; for (int i = 1; i <= n; ++i) { if (prefix_map.find(a[i] - k) != prefix_map.end()) { cout << prefix_map[a[i] - k] + 1 << " " << i << endl; return 0; } prefix_map[a[i]] = i; } cout << "No Solution" << endl; return 0; } ``` ### 说明 - 上述代码使用了前缀和和哈希表(`unordered_map`)来高效查找是否存在和为`k`的连续子数组。 - 时间复杂度为O(n),适用于大规模输入。 - 如果题目有其他特定要求,可以根据具体条件修改代码逻辑。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值