题目描述

Informatik verbindet dich und mich.
信息将你我连结。
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的值即可。

输入

第一行有三个整数n,m,p,c,所有整数含义见问题描述。
接下来一行n个整数,表示a数组的初始值。
接下来m行,每行三个整数,其中第一个整数表示了操作的类型。
如果是0的话,表示这是一个修改操作,操作的参数为l,r。
如果是1的话,表示这是一个询问操作,操作的参数为l,r。
1 ≤ n ≤ 50000, 1 ≤ m ≤ 50000, 1 ≤ p ≤ 100000000, 0 < c <p, 0 ≤ ai < p

输出

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

样例输入

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

样例输出

0
3


题解

扩展欧拉定理+并查集+树状数组

扩展欧拉定理:

通过各种证明可以得知,一个数n最多进行 log p 次操作后就会变为一个定值。

我们先预处理出成为定值的步数,不断求欧拉函数,记录每次变成了什么数,直至p=1.

此时需要继续迭代一层,作用后面讲。

然后再用递推法预处理出c...^ai(j个c) mod phi...(p)(k个phi),按照公式用三维数组储存。

这里需要注意:扩展欧拉定理仅在n≥phi(p)时成立,当n<phi(p)时,对应的解决方法就是不加等式右面phi(p)的一项(变成一个类似恒等式的东西)

这时需要在求幂次的同时记录一下是否超过了phi(p),即判断两数相乘时是否超过phi(p)。

这样预处理后可以开始处理操作了。

用一个数组记录一下每个数操作了多少次,如果达到了能够使值不变的次数,则不再进行更新。而对于操作中的所有数暴力修改即可。

因此需要一个数据结构,维护某一个数的下一个不能够使值不变(即操作次数没有达到某值)的数是什么。这个可以使用并查集来实现。

然后由于要求和,所以再使用一个树状数组来维护前缀和。

至于为什么求phi时要多迭代一项,具体原因比较复杂:

先看一个例子:n=1,p=3,c=2,a[1]=0时,正解为0、1、2、1、1...,而错解为0、1、2、2、2...

Why?这需要我们做这道题的根本思路。

迭代至phi=1,是因为x%1=0。因此20≡0(mod 1)。

如果按照错解的思路,下一步进行22^0 mod 1 mod 2=20 mod 1 mod 2,进而22^0 mod 1+1 mod 2=20 mod 1+1 mod 2

看起来似乎很对,但是仔细想想可以观察到:0<1,不能按照扩展欧拉定理加上phi的一项!。

所以应当是22^0 mod 1+1 mod 2=20 mod 1 mod 2,而这是不成立的。

so,仅仅迭代到1是错误的,而多迭代一项(或特判)就能解决该问题。

最后,总时间复杂度是O(nlognlog^2p),会TLE,究其原因是快速幂的logp的时间复杂度,具体优化方法:我们可以预处理出c^i mod phi...(p)和c^10000j mod phi...(p),然后找前半部分和后半部分。这样可以O(1)得到c的幂次。就能A了。

#include <cstdio>
#include <algorithm>
#define N 50010
using namespace std;
typedef long long ll;
int n , m , cnt[N] , tot;
ll p , c , a[N] , sum[N] , fa[N] , base[30] , v[N][30][30] , pf[N][30] , pg[N][30];
bool flag[N][30][30] , bf[N][30] , bg[N][30];
ll pow(int y , int k , bool &flag)
{
int tf = y % 10000 , tg = y / 10000;
flag = bf[tf][k] | bg[tg][k] | (pf[tf][k] * pg[tg][k] >= base[k]);
return pf[tf][k] * pg[tg][k] % base[k];
}
ll phi(ll x)
{
ll i , ans = x;
for(i = 2 ; i * i <= x ; i ++ )
{
if(x % i == 0)
{
ans = ans / i * (i - 1);
while(x % i == 0) x /= i;
}
}
if(x != 1) ans = ans / x * (x - 1);
return ans;
}
void update(int x , ll a)
{
int i;
for(i = x ; i <= n ; i += i & -i) sum[i] = (sum[i] + a) % p;
}
ll query(int x)
{
int i;
ll ans = 0;
for(i = x ; i ; i -= i & -i) ans = (ans + sum[i]) % p;
return ans;
}
void init()
{
int i , j , k;
for(i = 0 ; i <= tot ; i ++ )
{
pf[0][i] = 1;
for(j = 1 ; j <= 10000 ; j ++ )
bf[j][i] = bf[j - 1][i] | (pf[j - 1][i] * c >= base[i]) , pf[j][i] = pf[j - 1][i] * c % base[i];
pg[0][i] = 1 , pg[1][i] = pf[10000][i] , bg[1][i] = bf[10000][i];
for(j = 2 ; j <= 10000 ; j ++ )
bg[j][i] = bg[j - 1][i] | (pg[j - 1][i] * pg[1][i] >= base[i]) , pg[j][i] = pg[j - 1][i] * pg[1][i] % base[i];
}
for(i = 1 ; i <= n ; i ++ )
{
for(j = 0 ; j <= tot ; j ++ ) flag[i][0][j] = (a[i] >= base[j]) , v[i][0][j] = a[i] % base[j];
for(j = 1 ; j <= tot ; j ++ )
for(k = 0 ; k <= tot - j ; k ++ )
v[i][j][k] = pow(v[i][j - 1][k + 1] + (flag[i][j - 1][k + 1] ? base[k + 1] : 0) , k , flag[i][j][k]);
}
}
int find(int x)
{
return x == fa[x] ? x : fa[x] = find(fa[x]);
}
int main()
{
int i , opt , l , r;
scanf("%d%d%lld%lld" , &n , &m , &p , &c);
base[0] = p;
while(base[tot] != 1) tot ++ , base[tot] = phi(base[tot - 1]);
base[++tot] = 1;
for(i = 1 ; i <= n ; i ++ ) scanf("%lld" , &a[i]) , update(i , a[i]);
init();
for(i = 1 ; i <= n + 1 ; i ++ ) fa[i] = i;
while(m -- )
{
scanf("%d%d%d" , &opt , &l , &r);
if(opt == 0)
{
for(i = find(l) ; i <= r ; i = find(i + 1))
{
update(i , -v[i][cnt[i]][0]) , cnt[i] ++ , update(i , v[i][cnt[i]][0]);
if(cnt[i] == tot) fa[i] = find(i + 1);
}
}
else printf("%lld\n" , (query(r) - query(l - 1) + 2 * p) % p);
}
return 0;
}

【bzoj4869】[Shoi2017]相逢是问候 扩展欧拉定理+并查集+树状数组的更多相关文章

  1. bzoj 4869: [Shoi2017]相逢是问候 [扩展欧拉定理 线段树]

    4869: [Shoi2017]相逢是问候 题意:一个序列,支持区间\(a_i \leftarrow c^{a_i}\),区间求和.在模p意义下. 类似于开根操作,每次取phi在log次后就不变了. ...

  2. bzoj4869: [Shoi2017]相逢是问候(欧拉函数+线段树)

    这题是六省联考的...据说数据还出了点锅,心疼六省选手QAQ 首先要知道扩展欧拉定理... 可以发现每次区间操作都会使模数进行一次phi操作,而一个数最多取logp次phi就会变成1,这时后面的指数就 ...

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

    题目链接 BZOJ4869 题解 这题调得我怀疑人生,,结果就是因为某些地方\(sb\)地忘了取模 前置题目:BZOJ3884 扩展欧拉定理: \[c^a \equiv c^{a \mod \varp ...

  4. [LNOI] 相逢是问候 || 扩展欧拉函数+线段树

    原题为2017六省联考的D1T3 给出一个序列,m次操作,模数p和参数c 操作分为两种: 1.将[l,r]区间内的每个数x变为\(c^x\) 2.求[l,r]区间内数的和%p 首先,我们要了解一些数论 ...

  5. Bzoj4869: [Shoi2017]相逢是问候

    题面 传送门 Sol 摆定理 \[ a^b\equiv \begin{cases} a^{b\%\phi(p)}~~~~~~~~~~~gcd(a,p)=1\\ a^b~~~~~~~~~~~~~~~~~ ...

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

    [BZOJ4869]相逢是问候(线段树,欧拉定理) 题面 BZOJ 题解 根据欧拉定理递归计算(类似上帝与集合的正确用法) 所以我们可以用线段树维护区间最少的被更新的多少次 如果超过了\(\varph ...

  7. BZOJ:4869: [Shoi2017]相逢是问候

    4869: [Shoi2017]相逢是问候 先说点正经的…… 显然做了有限次(我只知道是有限次,而且不会大,别人说是log次?)修改以后会达到不动点,即以后怎么修改都不变了. 然后就随便做了.(3个l ...

  8. 牛客练习赛22-E.简单数据结构1(扩展欧拉定理降幂 +树状数组)

    链接:E.简单数据结构1 题意: 给一个长为n的序列,m次操作,每次操作: 1.区间加 2.对于区间,查询 ,一直到- 请注意每次的模数不同.   题解:扩展欧拉定理降幂 对一个数p取log(p)次的 ...

  9. NOJ——1669xor的难题(详细的树状数组扩展—异或求和)

    [1669] xor的难题 时间限制: 1000 ms 内存限制: 65535 K 问题描述 最近Alex学长有个问题被困扰了很久,就是有同学给他n个数,然后给你m个查询,然后每个查询给你l和r(左下 ...

随机推荐

  1. SRM 670 div2 A B C div1 A(贪心,子问题合并)

    A Cdgame brute force... B Drbalance 贪心,每次选最前面的-变成+,相当于后面所有的负值+2. C Treestrat 考虑集中去抓一个Red Token,以这个To ...

  2. noip模拟赛#39

    昨晚打开的题想了一会发现都不会后决定慢慢想.然后早上开校会的时候莫名其妙的都想出来了... T1:m=100,ai=50000,i<=5.1到m的数每个数只能用一次,判断是否能够有这些数的某些数 ...

  3. IPC Gateway 设计

    1. IPC Gateway对外提供的功能: IPC的register/request/reply/notification服务. 2. IPC Gatew的实现原理: 各个具体的服务注册自己的回调函 ...

  4. 《剑指offer》【调整数组顺序使奇数位于偶数前面】(python版)

    题目描述: 输入一个整数数组,实现一个函数来调整该数组中数字的顺序,使得所有的奇数位于数组的前半部分,所有的偶数位于位于数组的后半部分 思路: 我认真看了一下,题目应该是要求在原地调整,所以这里不能再 ...

  5. cuda api查询问题

    在查询CUDA运行时API的时候,我用360极速浏览器的时候搜索结果一直不出来,但是用火狐的话就很流畅,所以建议大家在开发时还是用火狐浏览器.

  6. C# StreamReader对象

    1.读取文件 输入流用于从外部源读取数据,在很多情况下,数据源可以是磁盘上的文件或网络的某些位置,任何可能发送数据的位置都可以是数据源,比如网络应用程序,web服务,甚至是控制台.StreamRead ...

  7. 更改zabbix-server的端口

    1.前言zabbix-server的默认端口号是10051.如果存在端口号冲突,需要更改端口号. 以下为更改端口号的步骤. 2.更改配置文件 通常用安装包,也就是yum方式部署的话,其默认的配置文件是 ...

  8. python简单实用gunicorn部署

    linux 安装 pyuthon 安装   pip install gunicorn manage.py 文件 from app import create_app app = create_app( ...

  9. 三、Shell 传递参数

    Shell 传递参数 我们可以在执行 Shell 脚本时,向脚本传递参数,脚本内获取参数的格式为:$n.n 代表一个数字,1 为执行脚本的第一个参数,2 为执行脚本的第二个参数,以此类推…… 实例 以 ...

  10. JZOJ 1267. 路障

    1267. 路障(block.pas/c/cpp) (File IO): input:block.in output:block.out Time Limits: 1000 ms  Memory Li ...