这题好强强啊,貌似是集训队原题?集训队原题当中值域是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. outer join test

    create table t1_outerjoin(a int, b int , c int); create table t2_outerjoin(a int); create table t3_o ...

  2. Android Preference 设置偏好全攻略

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

  3. hdu1010Tempter of the Bone(迷宫dfs+剪枝)

    Tempter of the Bone Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Othe ...

  4. VIN码识别:让VIN码采集so easy!

    近几年汽车后市场呈喷井式发展,在过去的半年,汽车后市场规模已高达万亿级,产业前景广阔,与此同时行业运营也受信息区域化.数据不统一的制约,让企业面临着效率低下.规模化运行困难的痛点. 在汽车配件市场中, ...

  5. 了解Python控制流语句——if语句

    控制流 截止到现在,在我们所看过的程序中,总是有一系列语句从上到下精确排列,并交由 Python 忠实地执行.如果你想改变这一工作流程,应该怎么做?就像这样的情况:你需要程序作出一些决定,并依据不同的 ...

  6. 【movable-area、movable-view】 可移动区域组件说明

    movable-area.movable-view 可移动区域组件 原型: <movable-area scale-area="[Boolean]"> <mova ...

  7. leetcode-生成括号(回溯算法)

     转载出处:https://blog.csdn.net/yanerhao/article/details/68561290 生成括号     给出 n 代表生成括号的对数,请你写出一个函数,使其能够生 ...

  8. Qt+opencv:读取、显示图像

    GitHub:点击下载完整代码 本文主要是使用Qt与opencv将图像进行显示在QT界面上. 程序运行后的界面如下所示: (由于只有打开图像之后,才能对图像进行翻转,所以程序设置为读取图像成功之后才能 ...

  9. 397. Longest Continuous Increasing Subsequence

    Description Give an integer array,find the longest increasing continuous subsequence in this array. ...

  10. VMWare Workstation新装CentOS 7无法联网解决办法

    按照这位博主的方法成功解决:http://blog.csdn.net/deniro_li/article/details/54632949