bzoj4869: [Shoi2017]相逢是问候(欧拉函数+线段树)
这题是六省联考的...据说数据还出了点锅,心疼六省选手QAQ
首先要知道扩展欧拉定理...
可以发现每次区间操作都会使模数进行一次phi操作,而一个数最多取logp次phi就会变成1,这时后面的指数就没有用了,以后这个数的答案就不会变化了,也就是说一个数最多只会进行log次修改,那么我们就可以用线段树维护,如果某棵子数的最小操作次数达到了使模数变成1的次数我们就不需要修改了。
但是我们发现快速幂还有一个log,如果不优化的话三个log很有可能TLE。这个时候就有新操作了,底数是一定的c,指数最大为1e9,那么我们可以预处理出c^1~c^10000,设t为c^10000,再预处理出t^1~t^10000,这样对于每个询问我们只需要拆成前后两部分分别在c和t的表里找到并乘起来就好了,这样之后一个点最多被修改logn次,线段树效率O(NlogN),总复杂度O(Nlog^2N)。
要注意的点(数据出锅的地方)是预处理的时候计算使模数变成1的最小操作次数也就是几次幂运算之后答案不变,必须预处理到phi(1)=1,不能预处理到phi(2)=1,因为如果序列中有0的话,它是<phi(2)的,这时候指数加上phi(2)可能会出错。所以需要递归到phi(1)=1的地方,这样即使指数是0,加一之后c^0和c^1都一定>=phi(2)。
有一些大爷的博客就给出了只递归到phi(2)=1的反例,如 链接。
因为对c进行不同次的幂操作的模数并不同,不能递推,所以要预处理的东西还有c的logp次幂操作,这个可以直接递归计算,因为递归层数不会超过logp,枚举序列中的数和模数为O(NlogN),快速幂已经预处理了,所以总的复杂度为O(NlogNP)。至于递归的时候如何判断指数是否大于phi(当前模数),因为2进行4次幂操作之后已经非常大了(远大于p),所以只需要判断接下来的递归次数是否大于5就好了(如果小于5还要判断最顶部那个序列里的数和c进行(递归层数-1)次幂操作的数乘起来是否大于phi(当前模数))。
还要预处理的就是p进行logp次操作途中的所有phi值,然后这题就完了
#include<iostream>
#include<cstring>
#include<cstdlib>
#include<cstdio>
#include<cmath>
#include<algorithm>
#define MOD(x) ((x)>=mod?(x)-mod:(x))
#define ll long long
using namespace std;
const int maxn=, inf=1e9;
struct poi{int sum, cnt;}tree[maxn<<];
int n, m, c, mod, cnt, ty, x, y;
int a[maxn], p[maxn], mi1[][maxn], mi2[][maxn], cmi[][maxn];
inline void read(int &k)
{
int f=; k=; char c=getchar();
while(c<'' || c>'') c=='-'&&(f=-), c=getchar();
while(c<='' && c>='') k=k*+c-'', c=getchar();
k*=f;
}
inline int min(int a, int b){return a<b?a:b;}
inline void pushup(int x)
{
tree[x].sum=tree[x<<].sum+tree[x<<|].sum;
tree[x].sum=MOD(tree[x].sum);
tree[x].cnt=min(tree[x<<].cnt, tree[x<<|].cnt);
}
void build(int x, int l, int r)
{
if(l==r){read(a[l]); tree[x].sum=a[l]; return;}
int mid=(l+r)>>;
build(x<<, l, mid); build(x<<|, mid+, r);
pushup(x);
}
inline int phi(int n)
{
int ans=n;
for(int i=;i*i<=n;i++)
if(!(n%i))
{
ans=ans/i*(i-);
while(!(n%i)) n/=i;
}
if(n>) ans=ans/n*(n-);
return ans;
}
inline int power(int n, int x){return 1ll*mi2[x][n/]*mi1[x][n%]%p[x];}
inline int getmi(int x, int y, int mod)
{
if(c==)return ; if(!y) return x%p[mod];
int nxt=min(y, x+); ll now=(nxt==y?x:c);
if(now>=p[mod+]) return power(getmi(x, y-, mod+)+p[mod+], mod);
for(int i=nxt-;i>=;i--)
{
ll t=now, now=;
for(int j=;j<=t;j++)
{
now*=c;
if(now>=p[mod+]) return power(getmi(x, y-, mod+)+p[mod+], mod);
}
}
return power(getmi(x, y-, mod+), mod);
}
void prepare()
{
p[]=mod; while(p[cnt]-) p[++cnt]=phi(p[cnt-]); p[++cnt]=;
for(int i=;i<=cnt;i++)
{
mi1[i][]=; for(int j=;j<=;j++) mi1[i][j]=1ll*mi1[i][j-]*c%p[i];
mi2[i][]=; for(int j=;j<=;j++) mi2[i][j]=1ll*mi2[i][j-]*mi1[i][]%p[i];
}
for(int i=;i<=n;i++)
for(int j=;j<=cnt;j++)
if(a[i]) cmi[j][i]=getmi(a[i], j, );
else cmi[j][i]=getmi(, j-, );
}
void update(int x, int l, int r, int cl, int cr)
{
if(tree[x].cnt>=cnt) return;
if(l==r){tree[x].cnt++, tree[x].sum=cmi[tree[x].cnt][l]; return;}
int mid=(l+r)>>;
if(cl<=mid) update(x<<, l, mid, cl, cr);
if(cr>mid) update(x<<|, mid+, r, cl, cr);
pushup(x);
}
inline int query(int x, int l, int r, int cl, int cr)
{
if(cl<=l && r<=cr) return tree[x].sum;
int mid=(l+r)>>, ret=;
if(cl<=mid) ret=query(x<<, l, mid, cl, cr);
if(cr>mid) ret+=query(x<<|, mid+, r, cl, cr), ret=MOD(ret);
return ret;
}
int main()
{
read(n); read(m); read(mod); read(c);
build(, , n); prepare();
for(int i=;i<=m;i++)
{
read(ty); read(x); read(y);
if(!ty) update(, , n, x, y);
else printf("%d\n", query(, , n, x, y));
}
}
bzoj4869: [Shoi2017]相逢是问候(欧拉函数+线段树)的更多相关文章
- BZOJ4869 [Shoi2017]相逢是问候 【扩展欧拉定理 + 线段树】
题目链接 BZOJ4869 题解 这题调得我怀疑人生,,结果就是因为某些地方\(sb\)地忘了取模 前置题目:BZOJ3884 扩展欧拉定理: \[c^a \equiv c^{a \mod \varp ...
- LightOJ 1370 Bi-shoe and Phi-shoe 欧拉函数+线段树
分析:对于每个数,找到欧拉函数值大于它的,且标号最小的,预处理欧拉函数,然后按值建线段树就可以了 #include <iostream> #include <stdio.h> ...
- loj1370(欧拉函数+线段树)
传送门:Bi-shoe and Phi-shoe 题意:给出多个n(1<=n<=1e6),求满足phi(x)>=n的最小的x之和. 分析:先预处理出1~1e6的欧拉函数,然后建立一颗 ...
- [BZOJ4026]dC Loves Number Theory 欧拉函数+线段树
链接 题意:给定长度为 \(n\) 的序列 A,每次求区间 \([l,r]\) 的乘积的欧拉函数 题解 考虑离线怎么搞,将询问按右端点排序,然后按顺序扫这个序列 对于每个 \(A_i\) ,枚举它的质 ...
- LOJ #2142. 「SHOI2017」相逢是问候(欧拉函数 + 线段树)
题意 给出一个长度为 \(n\) 的序列 \(\{a_i\}\) 以及一个数 \(p\) ,现在有 \(m\) 次操作,每次操作将 \([l, r]\) 区间内的 \(a_i\) 变成 \(c^{a_ ...
- [LNOI] 相逢是问候 || 扩展欧拉函数+线段树
原题为2017六省联考的D1T3 给出一个序列,m次操作,模数p和参数c 操作分为两种: 1.将[l,r]区间内的每个数x变为\(c^x\) 2.求[l,r]区间内数的和%p 首先,我们要了解一些数论 ...
- SHOI 2017 相逢是问候(扩展欧拉定理+线段树)
题意 https://loj.ac/problem/2142 思路 一个数如果要作为指数,那么它不能直接对模数取模,这是常识: 诸如 \(c^{c^{c^{c..}}}\) 的函数递增飞快,不是高精度 ...
- BZOJ 4034: [HAOI2015]树上操作 [欧拉序列 线段树]
题意: 操作 1 :把某个节点 x 的点权增加 a . 操作 2 :把某个节点 x 为根的子树中所有点的点权都增加 a . 操作 3 :询问某个节点 x 到根的路径中所有点的点权和. 显然树链剖分可做 ...
- BZOJ 4034 树上操作(树的欧拉序列+线段树)
刷个清新的数据结构题爽一爽? 题意: 有一棵点数为 N 的树,以点 1 为根,且树点有边权.然后有 M 个 操作,分为三种: 操作 1 :把某个节点 x 的点权增加 a . 操作 2 :把某个节点 x ...
随机推荐
- Flutter的Appbar
actions → List一个 Widget 列表,代表 Toolbar 中所显示的菜单,对于常用的菜单,通常使用 IconButton 来表示:对于不常用的菜单通常使用PopupMenuButto ...
- cookie,localStorage和sessionStorage区别
三者的异同 特性 Cookie localStorage sessionStorage 数据的生命期 一般由服务器生成,可设置失效时间.如果在浏览器端生成Cookie,默认是关闭浏览器后失效 除非被清 ...
- yml中driver-class-name: com.mysql.jdbc.Driver 解析不到的问题
当在idea中使用springboot的快捷创建方式时,选中了mysql 和jdbc 那么pom文件中会直接有 <dependency> <groupId>mysql</ ...
- DNS 到底怎么工作的? (How does dns work?)
其实这个问题每次看的时候都觉得很明白,但是很久之后就忘记了,所以这次准备记录下来.深入到这个过程的各个细节之中,以后多看看. Step 1 请求缓存信息: 当你在开始访问一个 www.baidu.co ...
- python数据结构与算法第十二天【快速排序】
1. 原理如图所示: 2.代码实现 def quick_sort(alist, start, end): """快速排序""" # 递归的退 ...
- fiddler 笔记-重定向
重定向功能:主要是进行会话的拦截,然后替换原始资源的功能 选择请求-到autoresponser面板-勾选 enable rules :add rules 设置如下: 2 在浏览器中请示url-页面跳 ...
- Lodop扁宽横向241mm*93mm这种怪异的纸张如何设置
Lodop中如果设置LODOP.SET_PRINT_PAGESIZE(2,'241mm','93mm','');,会发现实际的纸张和自己设置的不同,不只是打印机不识别,xps和pdf虚拟打印机也不能正 ...
- DotNetty 实现 Modbus TCP 系列 (三) Codecs & Handler
本文已收录至:开源 DotNetty 实现的 Modbus TCP/IP 协议 DotNetty 作为一个半成品,我们不需要关注细节的实现,只需要关注自己的业务即可,所以最主要的就是处理 Codecs ...
- codeforces484A
Bits CodeForces - 484A Let's denote as the number of bits set ('1' bits) in the binary representati ...
- Android View相关知识问答
Android View相关核心知识问答 Activity Window View之间的三角关系 你真的了解View的坐标吗? 在渲染前获取 View 的宽高 5种手势工具类 浅析Android的窗口