HDU5152 线段树 + 数论
题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=5152 ,线段树区间更新 + 点更新 + 数论知识(数论是重点QAQ),好题值得一做。
BestCoder Round #24的C题,一道神题,不得不说,出题人的数论学的很好,很多人都没想到2333333不是素数的问题,当时全场爆零。我今天下午开始研究这道题,后来看了好久的标程才懂,惭愧。
一共有两个操作一个询问:1.询问[l , r]区间里的值的和; 2.将点x的值a[x]赋为2a[x]; 3.将区间[l , r]都加上x。
这道题复杂就是操作2了,这里需要先知道一个数论知识: 当x >= Phi(C)时, A^x = A ^ (x%Phi(C) + Phi(C)) (mod C). Phi(C)是C的欧拉函数,即1 ~ C中与C互素整数的个数,具体求法百度之。所以当操作2一直累积下去的时候应该是这样:
所以就是一直迭代求2333333的欧拉函数,对于2333333这个模数来说,求18次欧拉函数后就变成了1,所以保存到19层即可。接下来就是线段树的更新与求和。
具体解法:
这里只介绍操作2的部分:标程中用了一个一维的vector向量来保存两个信息:每个位置的操作2的次数和操作3要加的数,具体实现方法是vector <LL> a[N]后,如果i号位置需要进行操作2,则进行操作a[i].pushback(0),如果操作3的PushDown()更新到i的位置时,则a[i][a[i].size() - 1] += add[rt]; 表示在a[i][]数组的最后一个位置加上要加的数。这样的话a[i][]数组的长度就表示有多少次操作2,保存的值就代表了当时的操作3加上的值,所以每次迭代应该是num = 2num % phi[pos] + phi[pos] + a[...] (说点有点乱,不好意思~)。
还有一点要注意的是x < Phi(C)的情况,这样的话A ^ x 还等于 A ^ x,这样的话迭代就变为 num = 2num + a[...]. 这时候再判断num与当前层的欧拉函数的大小关系,到满足条件时再像公式中的进行即可。这里要注意一点,如果当前层满足 x >= Phi(C)的情况,则以后的每一层迭代结果必然也满足,因为 x % Phi(C) + Phi(C) 必然要大于Phi(C),这样传递下去就可以保证以后的都满足了...(- - ..还是说的很乱,具体见代码,虽然都是看的标程编的QAQ)。
#include <iostream>
#include <cstdio>
#include <vector>
#include <cmath>
#include <string>
#include <string.h>
#include <algorithm>
using namespace std;
#define LL __int64
#define eps 1e-8
#define INF INT_MAX
#define lson l , m , rt << 1
#define rson m + 1 , r , rt << 1 | 1
const int MOD = ;
const int maxn = + ;
int phi[] = { , , , , , , ,
, , , , , , , , , , , };
LL sum[maxn << ] , add[maxn << ];
vector <LL> a[maxn];
int pow2[]; void init()
{ //求2 ^ i
for(int i = ; i <= ; i++)
pow2[i] = << i;
}
LL pow_mod(LL a , LL i , LL n)
{ // a ^ i % n的快速幂
if(i == )
return ;
LL tmp = pow_mod(a , i >> , n);
tmp = tmp * tmp % n;
if(i & )
tmp = tmp * a % n;
return tmp;
}
void PushUp(int rt)
{
sum[rt] = (sum[rt << ] + sum[rt << | ]) % MOD;
}
void build(int l , int r , int rt)
{
add[rt] = ;
if(l == r) {
a[l].clear(); //清零不要忘了
scanf("%d" , &sum[rt]);
a[l].push_back(sum[rt]); //初始值放入a[l][0]
sum[rt] %= MOD;
return;
}
int m = (l + r) >> ;
build(lson);
build(rson);
PushUp(rt);
}
void PushDown(int rt , int len)
{
if(add[rt]) {
add[rt << ] += add[rt];
add[rt << | ] += add[rt];
sum[rt << ] = (sum[rt << ] + 1LL * (len - (len >> )) * add[rt]) % MOD;
sum[rt << | ] = (sum[rt << | ] + 1LL * (len >> ) * add[rt]) % MOD;
add[rt] = ;
}
}
void update(int L , int R , int x , int l , int r , int rt)
{
if(L <= l && R >= r) {
sum[rt] = (sum[rt] + 1LL * (r - l + ) * x) % MOD;
add[rt] += x;
return;
}
PushDown(rt , r - l + );
int m = (l + r) >> ;
if(L > m)
update(L , R , x , rson);
else if(R <= m)
update(L , R , x , lson);
else {
update(L , R , x , lson);
update(L , R , x , rson);
}
PushUp(rt);
}
int cal(vector <LL> a)
{
LL num;
if(a.size() < ) { //没到18层,所以要全部来一遍
num = a[];
bool flag = false; //flag判断是否满足 x >= Phi(C)
int pos = a.size() - ;
if(num >= phi[pos]) {
flag = true;
num = num % phi[pos] + phi[pos];
}
pos--;
for(int i = ; i < a.size(); i++ , pos--) {
if(flag) {
num = (pow_mod( , num , phi[pos]) + a[i]) % phi[pos] + phi[pos];
} else {
if(num >= ) {
flag = true;
num = (pow_mod( , num , phi[pos]) + a[i]) % phi[pos] + phi[pos];
} else {
num = pow2[num] + a[i]; //这时就是 2 ^ num + a[i]
if(num >= phi[pos]) {
flag = true;
num = num % phi[pos] + phi[pos];
}
}
}
}
} else { //由于Phi[18]就等于1了,所以之前取余就都等于0,不管就可以了
num = ;
int pos = ;
for(int i = a.size() - ; i < a.size() ; i++ , pos--) {
num = (pow_mod( , num , phi[pos]) + a[i]) % phi[pos] + phi[pos];
}
}
return num % MOD;
}
void modify(int p , int l , int r , int rt)
{
if(l == r) {
if(add[rt]) { //保留操作3的信息
a[p][a[p].size() - ] += add[rt];
add[rt] = ;
}
a[p].push_back(); //加一层
sum[rt] = cal(a[p]);
return;
}
PushDown(rt , r - l + );
int m = (l + r) >> ;
if(p <= m)
modify(p , lson);
else
modify(p , rson);
PushUp(rt);
}
int query(int L , int R , int l , int r , int rt)
{
if(L <= l && R >= r) {
return sum[rt] % MOD;
}
PushDown(rt , r - l + );
int m = (l + r) >> ;
if(L > m)
return query(L , R , rson);
else if(R <= m)
return query(L , R , lson);
else
return (query(L , R , lson) + query(L , R , rson)) % MOD;
}
int main()
{
init();
int a , b , c , n , m , ch;
while(~scanf("%d %d" , &n , &m))
{
build( , n , );
while(m--) {
scanf("%d" , &ch);
if(ch == ) {
scanf("%d %d" , &a , &b);
printf("%d\n" , query(a , b , , n , ));
} else if(ch == ) {
scanf("%d" , &c);
modify(c , , n , );
} else {
scanf("%d %d %d" , &a , &b , &c);
update(a , b , c , , n , );
}
}
}
return ;
}
另附欧拉函数的代码:
int Euler(int n)
{
vector <int> a;
int i = ;
int res = n;
while(n != ) {
if(n % i == )
a.push_back(i);
while(n % i == )
n /= i;
i++;
}
for(i = ; i < a.size() ; i++)
res = res / a[i] * (a[i] - );
return res;
}
HDU5152 线段树 + 数论的更多相关文章
- ACM学习历程—Codeforces 446C DZY Loves Fibonacci Numbers(线段树 && 数论)
Description In mathematical terms, the sequence Fn of Fibonacci numbers is defined by the recurrence ...
- uoj#38. 【清华集训2014】奇数国(线段树+数论)
传送门 不难看出就是要先求区间积,再求这个区间积的\(\varphi\) 因为\(\varphi(x)=x\times\frac{p_1-1}{p_1}\times\frac{p_2-1}{p_2}\ ...
- HDU 5239 上海大都会 D题(线段树+数论)
打表,发现规律是存在一定次数(较小)后,会出现a=(a*a)%p.可以明显地发现本题与线段树有关.设置标记flag,记录本段内的数是否均已a=a*a%p.若是,则不需更新,否则更新有叶子结点,再pus ...
- RGCDQ(线段树+数论)
题意:求n和m之间的全部数的素因子个数的最大gcd值. 分析:这题好恶心.看着就是一颗线段树.但本题有一定的规律,我也是后来才发现,我还没推出这个规律.就不说了,就用纯线段树解答吧. 由于个点数都小于 ...
- 2017 ACM-ICPC, Universidad Nacional de Colombia Programming Contest K - Random Numbers (dfs序 线段树+数论)
Tamref love random numbers, but he hates recurrent relations, Tamref thinks that mainstream random g ...
- poj---(2886)Who Gets the Most Candies?(线段树+数论)
Who Gets the Most Candies? Time Limit: 5000MS Memory Limit: 131072K Total Submissions: 10373 Acc ...
- Bash and a Tough Math Puzzle CodeForces 914D 线段树+gcd数论
Bash and a Tough Math Puzzle CodeForces 914D 线段树+gcd数论 题意 给你一段数,然后小明去猜某一区间内的gcd,这里不一定是准确值,如果在这个区间内改变 ...
- codeforces 446C DZY Loves Fibonacci Numbers(数学 or 数论+线段树)(两种方法)
In mathematical terms, the sequence Fn of Fibonacci numbers is defined by the recurrence relation F1 ...
- Codeforces 446C DZY Loves Fibonacci Numbers [线段树,数论]
洛谷 Codeforces 思路 这题知道结论就是水题,不知道就是神仙题-- 斐波那契数有这样一个性质:\(f_{n+m}=f_{n+1}f_m+f_{n}f_{m-1}\). 至于怎么证明嘛-- 即 ...
随机推荐
- SQL Server 2012 安装——安装 OR 卸载
前言 上篇介绍了.net framework 3.5的安装,这次介绍一下,SQL Server2012的安装和彻底卸载.根据百度,下面是自己根据自己实际情况整理的安装步骤: 安装 见安装步骤( ...
- MCP|DYM|Quantitative mass spectrometry to interrogate proteomic heterogeneity in metastatic lung adenocarcinoma and validate a novel somatic mutation CDK12-G879V (利用定量质谱探究转移性肺腺瘤的蛋白质组异质性及验证新体细胞突变)
文献名:Quantitative mass spectrometry to interrogate proteomic heterogeneity in metastatic lung adenoca ...
- CLR Via C#: CLR 的执行模型
CLR(Common Language Runtime)公共语言运行时:是一个可由多种编程语言使用的“运行时”. 编译源代码文件的过程: C# 源代码文件 -> C# 编译器->托管模块( ...
- JMeter - 后处理器/脚本语言 - 比较
当我们使用JMeter / Response数据处理进行密集负载测试时,我们可能会非常小心我们选择的后处理器/脚本语言的类型.在这篇文章中,我想说明这些后处理器/脚本语言如何影响测试的整体性能. 我们 ...
- maven项目跳转页面报空指针错误 Servlet.service() for servlet 异常
Servlet.service() for servlet jsp threw exceptionjava.lang.NullPointerExceptionat org.jaronsource.ms ...
- 关于cookies,sessionStorage和localStorage的区别
如果我说得啰嗦了,请麻烦提醒我一下~~ 面试的时候说: 首先这三个可以在浏览器端按下F12,在Application可以查看到. 如下图: cookies: sessionStorage: sessi ...
- OpenLayers v4.2.0 -----地图延迟加载;
官方:http://openlayers.org/en/latest/examples/lazy-source.html <!DOCTYPE html> <html> < ...
- java动态线程池LinkedBlockingQueue和SynchronousQueue比较
import java.util.concurrent.Callable; public class MyCallable implements Callable<String> { pr ...
- python入门之random模块
#!/usr/bin/env python #_*_encoding: utf-8_*_ import random print(random.random()) #生成一个在0到1之间的随机浮点数 ...
- (转)linux 中特殊符号用法详解
linux 中特殊符号用法详解 原文:https://www.cnblogs.com/lidabo/p/4323979.html # 井号 (comments)#管理员 $普通用户 脚本中 #!/b ...