【题解】51nod 1203JZPLCM问题
这题好强强啊,貌似是集训队原题?集训队原题当中值域是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问题的更多相关文章
- 题解 51nod 1597 有限背包计数问题
题目传送门 题目大意 给出 \(n\),第 \(i\) 个数有 \(i\) 个,问凑出 \(n\) 的方案数. \(n\le 10^5\) 思路 呜呜呜,傻掉了... 首先想到根号分治,分别考虑 \( ...
- 2018 noip 备战日志
我是写给自己看的…… Day1 10.8 今天开始停晚修课了,开始认真备战考试了. 今天晚上效率不错,竟然不会累,应该是平时一直这个时间写作业大脑高度集中, 现在换了编程也一样可以集中到这个状态 一些 ...
- 51nod图论题解(4级,5级算法题)
51nod图论题解(4级,5级算法题) 1805 小树 基准时间限制:1.5 秒 空间限制:131072 KB 分值: 80 难度:5级算法题 她发现她的树的点上都有一个标号(从1到n),这些树都在空 ...
- 51nod 1812 树的双直径 题解【树形DP】【贪心】
老了-稍微麻烦一点的树形DP都想不到了. 题目描述 给定一棵树,边权是整数 \(c_i\) ,找出两条不相交的链(没有公共点),使得链长的乘积最大(链长定义为这条链上所有边的权值之和,如果这条链只有 ...
- 51NOD 1773:A国的贸易——题解
http://www.51nod.com/onlineJudge/questionCode.html#!problemId=1773 参考1:FWT讲解 https://www.cnblogs.com ...
- 51NOD 1934:受限制的排列——题解
http://www.51nod.com/onlineJudge/questionCode.html#!problemId=1934 听说会笛卡尔树的人这题都秒了啊…… 参考:https://blog ...
- 51NOD 1038:X^A Mod P——题解
http://www.51nod.com/onlineJudge/questionCode.html#!problemId=1038 X^A mod P = B,其中P为质数.给出P和A B,求< ...
- 51NOD 1353:树——题解
http://www.51nod.com/onlineJudge/questionCode.html#!problemId=1353 今天小a在纸上研究树的形态,众所周知的,有芭蕉树,樟树,函树,平衡 ...
- 【胡搞的不能AC的题解,暴力搜索一发博弈问题】1995 三子棋 - 51Nod
1995 三子棋 题目来源: syu校赛 基准时间限制:1 秒 空间限制:131072 KB 分值: 0 难度:基础题 原题链接: https://www.51nod.com/onlineJudge/ ...
随机推荐
- Visual studio 2010 TFS地址解析,让团队资源管理器不再显示IP地址
第一步: 找到名为hosts的配置文件(路径C:\Windows\System32\drivers\etc\hosts)用记事本打开并写入需要的配置,例如我用到的是TFS服务器的IP地址为192.16 ...
- Thymeleaf 模板引擎用法
学习.改良.极致 博客园 首页 新随笔 联系 管理 订阅 随笔- 31 文章- 0 评论- 50 Thymeleaf 常用属性 文章主目录 th:action th:each th:fiel ...
- Restify Api 开发经验
此文已由作者王振华授权网易云社区发布. 欢迎访问网易云社区,了解更多网易技术产品运营经验. 工作期间,一直在用Restify开发或维护大大小小的API系统,现在分享一下一些个人觉得不错的Tips. 充 ...
- vue2组件之间双向数据绑定问题
最近在使用element-ui的dialog组件二次封装成独立组件使用时,子组件需要将关闭dialog状态返回给父组件,简单的说就是要实现父子组件之间的数据双向绑定问题. 大致代码如下: 1,父组件 ...
- Unity编辑器 - 资源修改立即写入磁盘AssetDataBase.SaveAssets()
Unity编辑器 - 资源修改立即写入磁盘AssetDataBase.SaveAssets() 在编写编辑器时,如果需要修改Unity序列化资源(如Prefab,美术资源,ScriptableObje ...
- (Pyhton爬虫03)爬虫初识
原本的想法是这样的:博客整理知识学习的同时,也记录点心情...集中式学习就没这么多好记录的了! 要学习一门技术,首先要简单认识一下爬虫!其实可以参考爬虫第一章! 整体上介绍该技术包含技能,具体能做什么 ...
- 1. 两数之和【Leetcode中国,by java】
给定一个整数数组和一个目标值,找出数组中和为目标值的两个数. 你可以假设每个输入只对应一种答案,且同样的元素不能被重复利用. 示例: 给定 nums = [2, 7, 11, 15], target ...
- 《Effective C++》读书笔记 条款02 尽量以const,enum,inline替换#define
Effective C++在此条款中总结出两个结论 1.对于单纯常量,最好以const对象或enum替换#define 2.对于形似函数的宏,最好改用inline函数替换#define 接下来我们进行 ...
- Java学习笔记-11.运行期间类型鉴定
1.Class对象的getClasses()方法获取的是该类中所有的公共的内部类,以及从父类,父接口继承来的内部类.getinterfaces()方法返回类继承的所有接口. import javax. ...
- 2.重新安装CM服务
步骤1.停止CM服务2.删除CM服务3.添加CM服务4.测试数据库 步骤 1.停止CM服务 2.删除CM服务 没有发现可以单独删除某一项CM服务,必须全部删除 3.添加CM服务 4.测试数据库 如果报 ...