题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=5152 ,线段树区间更新 + 点更新 + 数论知识(数论是重点QAQ),好题值得一做。

  BestCoder Round #24的C题,一道神题,不得不说,出题人的数论学的很好,很多人都没想到2333333不是素数的问题,当时全场爆零。我今天下午开始研究这道题,后来看了好久的标程才懂,惭愧。


  一共有两个操作一个询问:1.询问[l , r]区间里的值的和; 2.将点x的值a[x]赋为2; 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一直累积下去的时候应该是这样:

           HDU5152 线段树 + 数论-LMLPHP

  所以就是一直迭代求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 = 2 + a[...] (说点有点乱,不好意思~)。

  还有一点要注意的是x < Phi(C)的情况,这样的话A ^ x 还等于 A ^ x,这样的话迭代就变为 num = 2 + 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;
}
05-21 00:29