题目链接: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 线段树 + 数论的更多相关文章

  1. ACM学习历程—Codeforces 446C DZY Loves Fibonacci Numbers(线段树 && 数论)

    Description In mathematical terms, the sequence Fn of Fibonacci numbers is defined by the recurrence ...

  2. uoj#38. 【清华集训2014】奇数国(线段树+数论)

    传送门 不难看出就是要先求区间积,再求这个区间积的\(\varphi\) 因为\(\varphi(x)=x\times\frac{p_1-1}{p_1}\times\frac{p_2-1}{p_2}\ ...

  3. HDU 5239 上海大都会 D题(线段树+数论)

    打表,发现规律是存在一定次数(较小)后,会出现a=(a*a)%p.可以明显地发现本题与线段树有关.设置标记flag,记录本段内的数是否均已a=a*a%p.若是,则不需更新,否则更新有叶子结点,再pus ...

  4. RGCDQ(线段树+数论)

    题意:求n和m之间的全部数的素因子个数的最大gcd值. 分析:这题好恶心.看着就是一颗线段树.但本题有一定的规律,我也是后来才发现,我还没推出这个规律.就不说了,就用纯线段树解答吧. 由于个点数都小于 ...

  5. 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 ...

  6. poj---(2886)Who Gets the Most Candies?(线段树+数论)

    Who Gets the Most Candies? Time Limit: 5000MS   Memory Limit: 131072K Total Submissions: 10373   Acc ...

  7. Bash and a Tough Math Puzzle CodeForces 914D 线段树+gcd数论

    Bash and a Tough Math Puzzle CodeForces 914D 线段树+gcd数论 题意 给你一段数,然后小明去猜某一区间内的gcd,这里不一定是准确值,如果在这个区间内改变 ...

  8. codeforces 446C DZY Loves Fibonacci Numbers(数学 or 数论+线段树)(两种方法)

    In mathematical terms, the sequence Fn of Fibonacci numbers is defined by the recurrence relation F1 ...

  9. Codeforces 446C DZY Loves Fibonacci Numbers [线段树,数论]

    洛谷 Codeforces 思路 这题知道结论就是水题,不知道就是神仙题-- 斐波那契数有这样一个性质:\(f_{n+m}=f_{n+1}f_m+f_{n}f_{m-1}\). 至于怎么证明嘛-- 即 ...

随机推荐

  1. 51nod1010(枚举+二分)

    题目链接:https://www.51nod.com/onlineJudge/questionCode.html#!problemId=1010 题意:中文题诶- 思路:求第一个比 x (1<= ...

  2. 莫队算法-小Z的袜子

    小Z的妹子袜子这道题用的是莫队算法,据说解决离线区间询问几乎无敌. 作为一个生活散漫的人,小Z每天早上都要耗费很久从一堆五颜六色的袜子中找出一双来穿.终于有一天,小Z再也无法忍受这恼人的找袜子过程,于 ...

  3. 洛谷P1022 计算器的改良

    P1022 计算器的改良 题目背景 NCL是一家专门从事计算器改良与升级的实验室,最近该实验室收到了某公司所委托的一个任务:需要在该公司某型号的计算器上加上解一元一次方程的功能.实验室将这个任务交给了 ...

  4. 解决Maven项目中jar包依赖冲突问题

    版本冲突的解决方案 [1]调节原则 [1]路径最短者优先原则 [2]路径相同时,先声明者优先原则 [2]排除原则:用于排除某项依赖的依赖jar包 <dependency> <grou ...

  5. PHP-CGI远程任意代码执行漏洞(CVE-2012-1823)修复方案

    首先介绍一下这个漏洞,其实是在apache调用php解释器解释.php文件时,会将url参数传我给php解释器,如果在url后加传命令行开关(例如-s.-d .-c或  -dauto_prepend_ ...

  6. Android Gradle 学习笔记(五):Gradle 任务 Task

    在之前的学习中,我们了解到Gradle的构建工作都是由Task组合完成的.本节我们就来介绍一下 Task - 任务. 一.多种方式创建任务 在Gradle中,我们可以有很多种方式来创建任务.为什么有这 ...

  7. 关于JSON可能出现的错误,待更/todo

    今天写到webAppService.js的时候,用fs模块调用json,发现单引号的json被警告了. json就是一段有格式的字符串,如果数据的封装与解析都是我们自己做的时候,单引号和双引号是没啥关 ...

  8. Repair 暴力

    Description standard input/outputStatements Alex is repairing his country house. He has a rectangula ...

  9. 牛客网Java刷题知识点之基本类型、引用类型

    不多说,直接上干货! byte-short-int-long,方便识记.

  10. JDK动态代理详解-依赖接口

    0. 原理分析 a). 自定义实现InvocationHandler类,实现代理类执行时的invoke方法 b). 使用Proxy.newProxyInstance生成接口的代理类(入参还包括Invo ...