【BZOJ4869】相逢是问候 [线段树]

相逢是问候

Time Limit: 40 Sec  Memory Limit: 512 MB
[Submit][Status][Discuss]

Description

  Informatikverbindetdichundmich.

  信息将你我连结。B君希望以维护一个长度为n的数组,这个数组的下标为从1到n的正整数。一共有m个操作,可以

  分为两种:0 l r表示将第l个到第r个数(al,al+1,...,ar)中的每一个数ai替换为c^ai,即c的ai次方,其中c是

  输入的一个常数,也就是执行赋值ai=c^ai1 l r求第l个到第r个数的和,也就是输出:sigma(ai),l<=i<=rai因为

  这个结果可能会很大,所以你只需要输出结果mod p的值即可。

Input

  第一行有三个整数n,m,p,c,所有整数含义见问题描述。

  接下来一行n个整数,表示a数组的初始值。

  接下来m行,每行三个整数,其中第一个整数表示了操作的类型。

  如果是0的话,表示这是一个修改操作,操作的参数为l,r。

  如果是1的话,表示这是一个询问操作,操作的参数为l,r。

Output

  对于每个询问操作,输出一行,包括一个整数表示答案mod p的值。

Sample Input

  4 4 7 2
  1 2 3 4
  0 1 4
  1 2 4
  0 1 4
  1 1 3

Sample Output

  0
  3

HINT

  1 ≤ n ≤ 50000, 1 ≤ m ≤ 50000, 1 ≤ p ≤ 100000000, 0 < c <p, 0 ≤ ai < p

Solution

  首先,我们运用欧拉定理:

  然后还有一个定理:一个数在执行log次操作后,值不会改变。

  于是乎,我们可以运用线段树,暴力修改每一个值,如果值都不变了则不修改。

  然后我们再考虑一下,怎么修改这个值:
  已知a(原值)times(修改次数),我们考虑每一次%什么,
    考虑每一次b的模数。
    首先如果b%phi(p),意味着a^b%p下同余。
    如果这一次b%phi(phi(p)),意味着a^bphi(p)下同余,
    同时也意味着下一次在%phi(p)意义下。
    我们要让答案最后是在%p意义下的,那么显然每次b%phi[times-1]
  再带上快速幂,那么这样效率就是O(nlog^3(n))的。

Code

#include<iostream>
#include<string>
#include<algorithm>
#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<cmath>
using namespace std;
typedef long long s64;

const int ONE = 500005;
const int INF = 2147483640;

int n,m,p,C;
int opt,x,y;
int a[ONE],phi[ONE],p_num;
int MOD;
int res;

struct power
{
        int value;
        int cnt;
}Node[ONE];

int get()
{
        int res=1,Q=1;char c;
        while( (c=getchar())<48 || c>57 )
        if(c==‘-‘)Q=-1;
        res=c-48;
        while( (c=getchar())>=48 && c<=57 )
        res=res*10+c-48;
        return res*Q;
}

int Getphi(int n)
{
        int res = n;
        for(int i=2; i*i<=n; i++)
        if(n % i == 0)
        {
            res = res/i*(i-1);
            while(n % i == 0) n /= i;
        }
        if(n != 1) res = res/n*(n-1);
        return res;
}

int Quickpow(int a,int b,int MOD)
{
        int res = 1;
        while(b)
        {
            if(b & 1) res = (s64)res * a % MOD;
            a = (s64)a * a % MOD;
            b >>= 1;
        }
        return res;
}

void Build(int i,int l,int r)
{
        if(l == r)
        {
            Node[i].value = a[l] % MOD;
            return;
        }
        int mid = l+r>>1;
        Build(i<<1, l, mid);
        Build(i<<1|1, mid + 1, r);
        Node[i].value = (Node[i<<1].value + Node[i<<1|1].value) % MOD;
}

int Calc(int a, int times)
{
        for(int i=times; i>=1; i--)
        {
            if(a >= phi[i]) a = a%phi[i] + phi[i];
            a = Quickpow(C, a, phi[i-1]);
            if(!a) a = phi[i-1];
        }
        return a;
}

void Update(int i,int l,int r,int L,int R)
{
        if(Node[i].cnt >= p_num) return;
        if(l == r)
        {
            Node[i].value = Calc(a[l], ++Node[i].cnt);
            return;
        }

        int mid = l+r>>1;
        if(L <= mid) Update(i<<1, l, mid, L, R);
        if(mid+1 <= R) Update(i<<1|1, mid+1, r, L, R);

        Node[i].value = (Node[i<<1].value + Node[i<<1|1].value) % MOD;
        Node[i].cnt = min(Node[i<<1].cnt, Node[i<<1|1].cnt);
}

void Query(int i,int l,int r,int L,int R)
{
        if(L<=l && r<=R)
        {
            res = (res + Node[i].value) % MOD;
            return;
        }

        int mid = l+r>>1;
        if(L <= mid) Query(i<<1, l, mid, L, R);
        if(mid+1 <= R) Query(i<<1|1, mid+1, r, L, R);
}

int main()
{
        n = get();    m = get();    p = get();    C = get();
        for(int i=1; i<=n; i++) a[i] = get();

        MOD = phi[0] = p;
        while(p!=1) phi[++p_num] = p = Getphi(p);
        phi[++p_num] = 1;
        Build(1, 1, n);

        while(m--)
        {
            opt = get();
            x = get();    y = get();
            if(!opt) Update(1, 1, n, x, y);
            else
            {
                res = 0;
                Query(1, 1, n, x, y);
                printf("%d\n", res);
            }
        }
}

时间: 2024-10-19 20:00:16

【BZOJ4869】相逢是问候 [线段树]的相关文章

[BZOJ4869][六省联考2017]相逢是问候(线段树+扩展欧拉定理)

4869: [Shoi2017]相逢是问候 Time Limit: 40 Sec  Memory Limit: 512 MBSubmit: 1313  Solved: 471[Submit][Status][Discuss] Description Informatikverbindetdichundmich. 信息将你我连结.B君希望以维护一个长度为n的数组,这个数组的下标为从1到n的正整数.一共有m个操作,可以 分为两种:0 l r表示将第l个到第r个数(al,al+1,...,ar)中的每

【bzoj4869】[Shoi2017]相逢是问候 线段树+扩展欧拉定理

Description Informatikverbindetdichundmich. 信息将你我连结.B君希望以维护一个长度为n的数组,这个数组的下标为从1到n的正整数.一共有m个操作,可以 分为两种:0 l r表示将第l个到第r个数(al,al+1,...,ar)中的每一个数ai替换为c^ai,即c的ai次方,其中c是 输入的一个常数,也就是执行赋值ai=c^ai1 l r求第l个到第r个数的和,也就是输出:sigma(ai),l<=i<=rai因为 这个结果可能会很大,所以你只需要输出结

洛谷P3747 [六省联考2017]相逢是问候

传送门 题解 扩展欧拉定理. 线段树维护,已经全改到底了的节点就不管,不然暴力修改下去. //Achen #include<algorithm> #include<iostream> #include<cstring> #include<cstdlib> #include<vector> #include<cstdio> #include<queue> #include<cmath> const int N=5

【BZOJ4869】相逢是问候(线段树,欧拉定理)

[BZOJ4869]相逢是问候(线段树,欧拉定理) 题面 BZOJ 题解 根据欧拉定理递归计算(类似上帝与集合的正确用法) 所以我们可以用线段树维护区间最少的被更新的多少次 如果超过了\(\varphi\)的限制 就不用再计算了 如果需要计算就每次暴力算 这样的复杂度\(O(nlog^2)\) #include<iostream> #include<cstdio> #include<cstdlib> #include<cstring> #include<

[六省联考2017]相逢是问候(线段树+拓展欧拉定理)

好题啊! 调了一个中午,发现有一条语句 \(RE\) 了.在 \(windows\) 下没关系,\(linux\) 下有问题,大大的问题. while(phi[tot]!=1) phi[++tot]=calc_phi(phi[tot-1]); 算是拓展欧拉定理的题吧.线段树只是一个工具,最主要还是暴力修改.因为 \(\varphi\) 不断套下去最多会有 \(\lfloor \log n\rfloor\) 层,所以我们对于每一层暴力算一遍,加上快速幂,时间复杂度 \(O(n\log^3 n)\)

bzoj千题计划271:bzoj4869: [六省联考2017]相逢是问候

http://www.lydsy.com/JudgeOnline/problem.php?id=4869 欧拉降幂+线段树,每个数最多降log次,模数就会降为1 #include<cmath> #include<cstdio> #include<iostream> using namespace std; #define N 50001 int n,m,p,c; int a[N]; int sum[N<<2]; int tag[N<<2]; in

[六省联考2017]相逢是问候

相逢是问候 2017-09-09 Description Informatikverbindetdichundmich. 信息将你我连结.B君希望以维护一个长度为n的数组,这个数组的下标为从1到n的正整数.一共有m个操作,可以分为两种:0 l r表示将第l个到第r个数(al,al+1,...,ar)中的每一个数ai替换为c^ai,即c的ai次方,其中c是输入的一个常数,也就是执行赋值ai=c^ai1 l r求第l个到第r个数的和,也就是输出:sigma(ai),l<=i<=rai因为这个结果可

[poj2104]可持久化线段树入门题(主席树)

解题关键:离线求区间第k小,主席树的经典裸题: 对主席树的理解:主席树维护的是一段序列中某个数字出现的次数,所以需要预先离散化,最好使用vector的erase和unique函数,很方便:如果求整段序列的第k小,我们会想到离散化二分和线段树的做法, 而主席树只是保存了序列的前缀和,排序之后,对序列的前缀分别做线段树,具有差分的性质,因此可以求任意区间的第k小,如果主席树维护索引,只需要求出某个数字在主席树中的位置,即为sort之后v中的索引:若要求第k大,建树时反向排序即可 1 #include

【BZOJ4942】[Noi2017]整数 线段树+DFS(卡过)

[BZOJ4942][Noi2017]整数 题目描述去uoj 题解:如果只有加法,那么直接暴力即可...(因为1的数量最多nlogn个) 先考虑加法,比较显然的做法就是将A二进制分解成log位,然后依次更新这log位,如果最高位依然有进位,那么找到最高位后面的第一个0,将中间的所有1变成0,那个0变成1.这个显然要用到线段树,但是复杂度是nlog2n的,肯定过不去. 于是我在考场上yy了一下,这log位是连续的,我们每次都要花费log的时间去修改一个岂不是很浪费?我们可以先在线段树上找到这段区间