这题好强强啊,貌似是集训队原题?集训队原题当中值域是1e9的范围,这样各种乱搞是妥妥的不能过了,只能写正解的离线+树状数组维护前缀积。

  最开始我写了几种乱搞做法,包括莫队和线段树做法。其中表现比较优秀的是线段树的做法,非常的暴力,就是每一个区间都维护vector记录区间lcm的质因数分解结果合并区间归并排序一路维护就可以了。由于51nod数据比较弱,数据值域大小有限,所以还可以跑过大部分的点,但A掉仍然是不能够。

  线段树乱搞:

#include <bits/stdc++.h>
using namespace std;
#define maxn 1000000
#define mod 1000000007
#define LL long long
int n, m, a[maxn];
int tot, rec[maxn], prime[maxn];
bool is_prime[maxn]; int read()
{
int x = , k = ;
char c;
c = getchar();
while(c < '' || c > '') { if(c == '-') k = -; c = getchar(); }
while(c >= '' && c <= '') x = x * + c - '', c = getchar();
return x * k;
} struct node
{
int num, cnt;
node(int a = , int b = ) { num = a, cnt = b; }
friend bool operator <(const node& a, const node& b)
{ if(a.num != b.num) return a.num < b.num; return a.cnt > b.cnt; }
friend bool operator ==(const node& a, const node& b)
{ return a.num == b.num; }
};
vector <node> T[maxn], S, tem1, tem2; void Get_Prime()
{
for(int i = ; i < maxn; i ++)
{
if(!is_prime[i]) prime[++ tot] = i, rec[i] = i;
for(int j = ; j <= tot; j ++)
{
if(i * prime[j] >= maxn) break;
is_prime[i * prime[j]] = ;
rec[i * prime[j]] = prime[j];
if(!(i % prime[j])) break;
}
}
} LL Qpow(LL x, int times)
{
LL base = ;
for(; times; times >>= , x = (x * x) % mod)
if(times & ) base = (base * x) % mod;
return base;
} void Push(vector <node> &S, node a)
{
if(!S.size() || a.num != S[S.size() - ].num) S.push_back(a);
} void Merge(vector <node> &M1, vector <node> &M2)
{
int i = , j = ;
tem1.resize(), tem2.resize();
for(int i = ; i < M1.size(); i ++) tem1.push_back(M1[i]);
for(int i = ; i < M2.size(); i ++) tem2.push_back(M2[i]);
M1.resize();
while(i < tem1.size() && j < tem2.size())
{
if(tem1[i] < tem2[j]) Push(M1, tem1[i]), i ++;
else Push(M1, tem2[j]), j ++;
}
while(i < tem1.size()) Push(M1, tem1[i]), i ++;
while(j < tem2.size()) Push(M1, tem2[j]), j ++;
} void Build(int p, int l, int r)
{
if(l == r)
{
int tem = a[l], last = -;
while(tem != )
{
if(T[p].size() && rec[tem] == T[p][last].num) T[p][last].cnt ++;
else T[p].push_back(node(rec[tem], )), last ++;
tem /= rec[tem];
}
return;
}
int mid = (l + r) >> ;
Build(p << , l, mid), Build(p << | , mid + , r);
T[p].resize();
for(int i = ; i < T[p << ].size(); i ++) T[p].push_back(T[p << ][i]);
Merge(T[p], T[p << | ]);
} void Query(int p, int l, int r, int L, int R)
{
if(L <= l && R >= r)
{
Merge(S, T[p]);
return;
}
if(L > r || R < l) return;
int mid = (l + r) >> ;
Query(p << , l, mid, L, R), Query(p << | , mid + , r, L, R);
} signed main()
{
Get_Prime();
n = read(), m = read();
for(int i = ; i <= n; i ++) a[i] = read();
Build(, , n);
for(int i = ; i <= m; i ++)
{
int L = read(), R = read();
S.resize();
Query(, , n, L, R);
LL ret = ;
for(int i = ; i < S.size(); i ++) ret = (ret * Qpow(S[i].num, S[i].cnt)) % mod;
printf("%lld\n", ret);
}
return ;
}

  现在来介绍一下正解的做法。有一个很妙的转化:lcm为各个数质因数分解之后每一位取max之后相乘,这样不是很好做。但我们对于一个数\(p^{a}\),可以分析发现它对于答案的贡献正好就是\(a\)次。我们可以把它看做是\(p^{1}, p^{2}...p^{a}\)这样\(a\)个不同的数,每个数对于答案均有\(*p\)的贡献(其中一个区间内相同的数只产生一次贡献)。

  那么现在我们的问题转化为了问一个区间中有多少个不同的数并将它们的权值累乘起来。一个区间当中有多少个不同的数,这里有一个常见的转化,即为统计一个区间当中有多少个\(last[i] < l\)的数(\(last[i]\) 表示 \(i\) 这个数字上一次出现的位置,没有出现可以视作 \(0\))。那么我们可以离线 + 树状数组,按照左端点排序依次处理。维护一个指针 \(now\),树状数组中维护的值为所有范围在 \(now --> tot\) 的数字之间 \(last[i] < now\) 的数字权值积。如何维护?只需要在一个数 \(< now\) 被从树状数组中删去的时候向树状数组中加入它的后继元素即可。

#include <bits/stdc++.h>
using namespace std;
#define maxn 50005
#define mod 1000000007
#define LL long long
#define lowbit(i) (i & (-i))
int n, m, tot, a[maxn << ], b[maxn << ];
int C[maxn << ], nxt[maxn << ], last[maxn << ];
int l[maxn], r[maxn], rec[maxn], ans[maxn]; int read()
{
int x = , k = ;
char c;
c = getchar();
while(c < '' || c > '') { if(c == '-') k = -; c = getchar(); }
while(c >= '' && c <= '') x = x * + c - '', c = getchar();
return x * k;
} struct node
{
int l, r, id;
node(int a = , int b = , int c = ) { l = a, r = b, id = c; }
friend bool operator <(const node& x, const node& y)
{ return x.l < y.l; }
}Q[maxn]; int Qpow(int x, int times)
{
int base = ;
for(; times; times >>= , x = ((LL) x * x) % mod)
if(times & ) base = ((LL) base * x) % mod;
return base;
} void update(int x, int y)
{
for(; x <= tot; x += lowbit(x))
C[x] = (LL) C[x] * y % mod;
} int query(int x)
{
int ret = ;
for(; x; x -= lowbit(x)) ret = (LL) ret * C[x] % mod;
return ret;
} int main()
{
n = read(), m = read();
for(int i = ; i <= n; i ++)
{
int x = read(), t = sqrt(x);
if(x == ) continue;
l[i] = tot + ;
for(int j = ; j <= t; j ++)
{
if(!(x % j))
{
int k = j;
while(!(x % j))
{
x /= j; a[++ tot] = k;
b[tot] = j; k *= j;
}
}
}
if(x > ) ++ tot, a[tot] = b[tot] = x;
r[i] = tot;
}
for(int i = ; i <= tot; i ++) C[i] = ;
for(int i = ; i <= tot; i ++)
{
if(rec[a[i]]) nxt[rec[a[i]]] = i;
else update(i, b[i]);
rec[a[i]] = i;
}
for(int i = ; i <= m; i ++)
{
int x = read(), y = read();
Q[i] = node(l[x], r[y], i);
}
sort(Q + , Q + + m);
for(int i = , j = ; i <= tot; i ++)
{
while(Q[j + ].l == i) ++ j, ans[Q[j].id] = query(Q[j].r);
update(i, Qpow(b[i], mod - ));
if(nxt[i]) update(nxt[i], b[nxt[i]]);
}
for(int i = ; i <= m; i ++)
printf("%d\n", ans[i]);
return ;
}

  其实感觉这么多的题目做下来这个转化还是比较常见的。当一个数\(p\)对于它的\(k\)倍 \ \(k\) 次方产生为 \(k\) 的贡献时,我们就可以考虑将其看做 \(p, 2 * p, 3 * p...\) 或 \(p^{1}, p^{2}, p^{3}...\) 这么些不同的数字分别对它产生了为 \(1\) 的贡献,累加或累乘(贡献为乘积形式时)即可。如 Test 4 的 第一题、对于 \(N!\) 分解质因数等。

  而这个 \(last[i] < l\) 对于处理“在区间中只出现了一次”也是一个很棒也很常见的思路。好题!好好记录一下( • ̀ω•́ )✧

【题解】51nod 1203JZPLCM问题的更多相关文章

  1. 题解 51nod 1597 有限背包计数问题

    题目传送门 题目大意 给出 \(n\),第 \(i\) 个数有 \(i\) 个,问凑出 \(n\) 的方案数. \(n\le 10^5\) 思路 呜呜呜,傻掉了... 首先想到根号分治,分别考虑 \( ...

  2. 2018 noip 备战日志

    我是写给自己看的…… Day1 10.8 今天开始停晚修课了,开始认真备战考试了. 今天晚上效率不错,竟然不会累,应该是平时一直这个时间写作业大脑高度集中, 现在换了编程也一样可以集中到这个状态 一些 ...

  3. 51nod图论题解(4级,5级算法题)

    51nod图论题解(4级,5级算法题) 1805 小树 基准时间限制:1.5 秒 空间限制:131072 KB 分值: 80 难度:5级算法题 她发现她的树的点上都有一个标号(从1到n),这些树都在空 ...

  4. 51nod 1812 树的双直径 题解【树形DP】【贪心】

    老了-稍微麻烦一点的树形DP都想不到了. 题目描述 给定一棵树,边权是整数 \(c_i\) ,找出两条不相交的链(没有公共点),使得链长的乘积最大(链长定义为这条链上所有边的权值之和,如果这条链只有 ...

  5. 51NOD 1773:A国的贸易——题解

    http://www.51nod.com/onlineJudge/questionCode.html#!problemId=1773 参考1:FWT讲解 https://www.cnblogs.com ...

  6. 51NOD 1934:受限制的排列——题解

    http://www.51nod.com/onlineJudge/questionCode.html#!problemId=1934 听说会笛卡尔树的人这题都秒了啊…… 参考:https://blog ...

  7. 51NOD 1038:X^A Mod P——题解

    http://www.51nod.com/onlineJudge/questionCode.html#!problemId=1038 X^A mod P = B,其中P为质数.给出P和A B,求< ...

  8. 51NOD 1353:树——题解

    http://www.51nod.com/onlineJudge/questionCode.html#!problemId=1353 今天小a在纸上研究树的形态,众所周知的,有芭蕉树,樟树,函树,平衡 ...

  9. 【胡搞的不能AC的题解,暴力搜索一发博弈问题】1995 三子棋 - 51Nod

    1995 三子棋 题目来源: syu校赛 基准时间限制:1 秒 空间限制:131072 KB 分值: 0 难度:基础题 原题链接: https://www.51nod.com/onlineJudge/ ...

随机推荐

  1. Java:IDEA设置虚拟机运行时参数

    第一步:打开“Run->Edit Configurations”菜单 第二步:选择“VM Options”选项,输入你要设置的VM参数 第三步:点击“OK”.“Apply”后设置完成

  2. Android Preference 设置偏好全攻略

    Android 设置是每个App必不可小的东西,看似很简单,但是初学不熟悉的很花时间去研究,特别样式兼容方面,以及有自定义设置的需求,下面是对用法做一个总结 Preference结构 界面结构看下图 ...

  3. 「日常训练」Kefa and Park(Codeforces Round #321 Div. 2 C)

    题意与分析(CodeForces 580C) 给你一棵树,然后每个叶子节点会有一家餐馆:你讨厌猫(waht?怎么会有人讨厌猫),就不会走有连续超过m个节点有猫的路.然后问你最多去几家饭店. 这题我写的 ...

  4. git基础(2)

    三.查看提交历史日志查看·提交历史:git log 命令一个常用的选项是 -p,用来显示每次提交的内容差异. 你也可以加上 -2 来仅显示最近两次提交如果你想看到每次提交的简略的统计信息,你可以使用 ...

  5. (Python爬虫05)完善的爬虫学习大纲

  6. python内建模块Collections

    # -*- coding:utf-8 -*- # OrderedDict可以实现一个FIFO(先进先出)的dict, # 当容量超出限制时,先删除最早添加的Key: from collections ...

  7. vmware centOS上网配置笔记

    ⦁    修改/etc/sysconfig/network-scripts/ifcfg-eth0文件  (首先查看本机vmware 虚拟网络编辑器中的网关) 重启网络 使用命令:service net ...

  8. 1.安装hbase

    参考:http://hbase.apache.org/book.html#quickstart 一.下载hbase 去apache下载hbase,然后解压到/usr/local/hbase-1.1.3 ...

  9. 【IdentityServer4文档】- 欢迎来到 IdentityServer4

    欢迎来到 IdentityServer4 IdentityServer4 是一款包含和实现了 OpenID Connect 和 OAuth 2.0 协议的,适用于 ASP.NET Core 的框架 . ...

  10. DNS域名解析协议

    一. 根域 就是所谓的“.”,其实我们的网址www.baidu.com在配置当中应该是www.baidu.com.(最后有一点),一般我们在浏览器里输入时会省略后面的点,而这也已经成为了习惯. 根域服 ...