【2019北京集训2】Elephant 平衡树
题目大意:给你一个长度为$n$的序列$A_i$,有$q$次操作,每次操作为以下三种之一:
询问区间的$F_M(A_i)$的最大公约数。
区间翻转,区间加一个正数。
我们定义$gcd(0,0)=0$,且$F_M(A_i)$为在一个$M$个点的无向完全图中从第一个点开始走$k$步后回到第一个点的方案数。
数据范围:$n,q≤10^5$,$0≤A_i≤10^8$,$2≤M≤10^9$。
我们先考虑下如何求$F_M(x)$。
经过打表(大雾),我们发现:
若$x$为偶数,则$F_M(x)=M\times(F_M(x-1)+1)$
若$x$为奇数,则$F_M(x)=M\times(F_M(x-1)-1)$
特别地,$F_M(0)=1$,$F_M(1)=0$
我们显然可以$O(2\times 10^8)$预处理或者直接矩阵快速幂计算。
继续打表,我们发现:$gcd(F_M(x-1),F_M(y-1))=F_M(gcd(x-1,y-1))$,感兴趣的同学可以证明一下(反正我不会)
我们发现,如果询问的区间中存在数字0,则这个区间的GCD显然为1
考虑到要资瓷区间翻转,区间查询0的个数,区间抹去0,对于这部分我们需要单独开一棵splay来维护。
考虑维护>1部分的数。
我们先不考虑翻转的情况。
我们将差分后的数列丢入一棵splay中,每个节点维护整棵子树内的权值和,还有子树内的GCD
我们需要查询区间$[l,r]$的$GCD$时,我们只需要查询$[l+1,r]$的区间GCD,然后再和第$l$个位置的值求GCD即可输出。
原因显然
考虑翻转区间$[l,r]$的情况:不难发现受到影响的区间为$[l,r+1]$。
然后,经过冷静分析(大雾),我们发现翻转前后有这样的性质:
1,第$l$个位置的查分值和第$r+1$个位置的差分值需要单独更新。
2,区间$[l+1,r]$内的差分值等于原区间$[l+1,r]$内的差分值翻转再取相反数。
上面这张图是一个例子,证明显然。
然后,我们开几个标记打一下就可以了。
然后就没有然后了,注意细节
时间复杂度:$O(n\log^2\ n)$
#include<bits/stdc++.h>
#define M 200005
#define MOD 323232323
#define L long long
#define lc(x) ch[(x)][0]
#define rc(x) ch[(x)][1]
using namespace std; int GCD(int x,int y){return __gcd(abs(x),abs(y));} struct mat{
L a[][]; mat(){memset(a,,sizeof(a));}
void danwei(){a[][]=a[][]=a[][]=;}
friend mat operator *(mat a,mat b){
mat c;
for(int i=;i<;i++)
for(int j=;j<;j++)
for(int k=;k<;k++)
c.a[i][j]=(c.a[i][j]+a.a[i][k]*b.a[k][j])%MOD;
return c;
}
friend mat operator ^(mat x,int b){
mat ans; ans.danwei();
while(b){
if(b&) ans=ans*x;
x=x*x; b>>=;
}
return ans;
}
};
L P;
L getans(int n){
if(n==) return ;
mat a;
a.a[][]=a.a[][]=;
a.a[][]=P*P%MOD;
a.a[][]=(P-+MOD)%MOD;
a=a^(n-);
L res=a.a[][]*P%MOD;
if(n&) res=P*(res+)%MOD;
return res;
} namespace FF{
int siz[M]={},ch[M][]={},sum[M]={},tagf[M]={},val[M]={},rev[M]={},fa[M]={},use=; int root=;
void pushup(int x){
siz[x]=siz[lc(x)]+siz[rc(x)]+;
sum[x]=sum[lc(x)]+sum[rc(x)]+val[x];
}
void upd(int x){rev[x]^=; swap(lc(x),rc(x)); }
void cls(int x){tagf[x]=; sum[x]=val[x]=;}
void pushdown(int x){
if(rev[x]) upd(lc(x)),upd(rc(x)); rev[x]=;
if(tagf[x]) cls(lc(x)),cls(rc(x)); tagf[x]=;
}
void rotate(int x,int &k){
int y=fa[x],z=fa[y],l,r;
l=(ch[y][]!=x); r=l^;
if(y==k) k=x;
else{
if(lc(z)==y) ch[z][]=x;
else ch[z][]=x;
}
fa[y]=x; fa[x]=z; fa[ch[x][r]]=y;
ch[y][l]=ch[x][r]; ch[x][r]=y;
pushup(y); pushup(x);
}
void pud(int x,int k){if(x!=k) pud(fa[x],k); pushdown(x);} void splay(int x,int &k){
pud(x,k);
while(x!=k){
int y=fa[x],z=fa[y];
if(y!=k){
if((lc(y)==x)==(lc(z)==y)) rotate(y,k);
else rotate(x,k);
}
rotate(x,k);
}
}
int insert(int x,int id,int zhi){
pushdown(x);
if(!x){x=++use; val[x]=zhi;}
else{
if(siz[lc(x)]+<=id) rc(x)=insert(rc(x),id-siz[lc(x)]-,zhi),fa[rc(x)]=x;
else lc(x)=insert(lc(x),id,zhi),fa[lc(x)]=x;
}
pushup(x); return x;
}
int find(int x,int k){
pushdown(x);
if(siz[lc(x)]+==k) return x;
if(siz[lc(x)]>=k) return find(lc(x),k);
return find(rc(x),k-siz[lc(x)]-);
}
void ins(int x,int k){
root=insert(root,k,x);
splay(use,root);
} int query(int x,int y){
int X=find(root,x),Y=find(root,y+);
splay(X,root); splay(Y,rc(root));
return sum[lc(Y)];
}
void updata(int x,int y){
int X=find(root,x),Y=find(root,y+);
splay(X,root); splay(Y,rc(root));
cls(lc(Y));
pushup(Y);
pushup(root);
}
void setrev(int x,int y){
int X=find(root,x),Y=find(root,y+);
splay(X,root); splay(Y,rc(root));
upd(lc(Y));
}
} int siz[M]={},ch[M][]={},gcd[M]={},sum[M]={},val[M]={},rev[M]={},fa[M]={},use=; int root=;
void pushup(int x){
siz[x]=siz[lc(x)]+siz[rc(x)]+;
sum[x]=sum[lc(x)]+sum[rc(x)]+val[x];
gcd[x]=GCD(GCD(gcd[lc(x)],gcd[rc(x)]),val[x]);
}
void upd(int x){rev[x]^=; swap(lc(x),rc(x)); sum[x]=-sum[x]; val[x]=-val[x];}
void pushdown(int x){if(rev[x]) upd(lc(x)),upd(rc(x)); rev[x]=;}
void rotate(int x,int &k){
int y=fa[x],z=fa[y],l,r;
l=(ch[y][]!=x); r=l^;
if(y==k) k=x;
else{
if(lc(z)==y) ch[z][]=x;
else ch[z][]=x;
}
fa[y]=x; fa[x]=z; fa[ch[x][r]]=y;
ch[y][l]=ch[x][r]; ch[x][r]=y;
pushup(y); pushup(x);
}
void pud(int x){if(x!=root) pud(fa[x]); pushdown(x);} void splay(int x,int &k){
pud(x);
while(x!=k){
int y=fa[x],z=fa[y];
if(y!=k){
if((lc(y)==x)==(lc(z)==y)) rotate(y,k);
else rotate(x,k);
}
rotate(x,k);
}
}
int insert(int x,int id,int zhi){
pushdown(x);
if(!x){x=++use; val[x]=zhi;}
else{
if(siz[lc(x)]+<=id) rc(x)=insert(rc(x),id-siz[lc(x)]-,zhi),fa[rc(x)]=x;
else lc(x)=insert(lc(x),id,zhi),fa[lc(x)]=x;
}
pushup(x); return x;
}
int find(int x,int k){
pushdown(x);
if(siz[lc(x)]+==k) return x;
if(siz[lc(x)]>=k) return find(lc(x),k);
return find(rc(x),k-siz[lc(x)]-);
}
void ins(int x,int k){
root=insert(root,k,x);
splay(use,root);
}
void del(int k){
int x=find(root,k); splay(x,root);
int y=find(root,k+); lc(y)=lc(x);
fa[lc(x)]=y; fa[rc(x)]=; root=rc(x);
splay(y,root); } int getval(int id){
int x=find(root,id+);
splay(x,root);
return val[x]+sum[lc(x)];
}
int query(int x,int y){
if(x>y) return ;
int X=find(root,x),Y=find(root,y+);
splay(X,root);
splay(Y,rc(root));
return gcd[lc(Y)];
}
void updata(int id,int delta){
int x=find(root,id);
splay(x,root);
val[x]+=delta;
pushup(x);
} void setrev(int x,int y){
int X=find(root,x),Y=find(root,y+);
splay(X,root);
splay(Y,rc(root));
upd(lc(Y));
pushup(lc(Y));
splay(lc(Y),root);
} void setval(int x,int V){
int X=find(root,x+);
splay(X,root);
val[X]=V;
pushup(X);
} int n,m,a[M]={}; int main(){
ins(,); ins(,);
FF::ins(,); FF::ins(,);
int cas; scanf("%d",&cas);
scanf("%d%d%d",&P,&n,&m); P--;
for(int i=;i<=n;i++) scanf("%d",a+i),a[i]--; for(int i=;i<=n+;i++){
ins(a[i]-a[i-],i);
FF::ins(a[i]==-,i);
} while(m--){
int op,x,y;
scanf("%d%d%d",&op,&x,&y);
if(op==){
int F=FF::query(x,y);
if(F) {printf("1\n"); continue;}
int ans=query(x+,y);
int las=getval(x);
printf("%lld\n",getans(GCD(las,ans)));
}
if(op==){
int k; scanf("%d",&k);
updata(x+,k);
updata(y+,-k);
FF::updata(x,y);
}
if(op==){
if(x==y) continue;
int Vl=getval(x-);
int VR=getval(y);
int Vr=getval(y+);
setrev(x+,y);
FF::setrev(x,y);
setval(x,VR-Vl); VR=getval(y); setval(y+,Vr-VR);
}
}
}
【2019北京集训2】Elephant 平衡树的更多相关文章
- 【2019北京集训测试赛(十三)】数据(sj) 冷静分析
题目大意:给你一个代表区间$[1,n]$的线段树,问你随机访问区间$[1,n]$中的一个子区间,覆盖到的线段树节点个数的期望(需要乘上$\frac{n(n-1)}{2}$后输出). 数据范围:$n≤1 ...
- 【2019北京集训测试赛(七)】 操作 分治+FFT+生成函数
题目大意:你有$n$个操作和一个初始为$0$的变量$x$. 第$i$个操作为:以$P_i$的概率给$x$加上$A_i$,剩下$1-P_i$的概率给$x$乘上$B_i$. 你袭击生成了一个长度为$n$的 ...
- 【2019北京集训六】路径(path) 二分+DP
此题niubi! 题目大意:给你一颗n个点的点带权无根树,现在请您进行以下两步操作: 1,选择一个$[0,T]$之间的整数$C$,并令所有的点权$wi$变为$(wi+C)%MOD$ 2,选择若干条点不 ...
- 【2019北京集训3】逻辑 树剖+2-sat
题目大意:有一颗有$m$个叶子节点的二叉树. 对于叶子节点$i$,$x[i]=(a[i]\ xor\ V_{p[i]})or(b[i]\ xor\ V_{q[i]})$ 对于非叶子节点$i$,$x[i ...
- 【2019北京集训2】duck 线段树优化建图+tarjan
题目大意:给你$n$个点,第$i$个点有点权$v_i$.你需要将这$n$个点排成一排,第$i$个点的点权能被累加当且仅当这个点前面存在编号在$[l_i,r_i]$中的点,问你这些点应该如何排列,点权和 ...
- 【北京集训D2T3】tvt
[北京集训D2T3]tvt \(n,q \le 1e9\) 题目分析: 首先需要对两条路径求交,对给出的四个点的6个lca进行分类讨论.易于发现路径的交就是这六个lca里面最深的两个所形成的链. 然后 ...
- (2016北京集训十)【xsy1528】azelso - 概率期望dp
北京集训的题都是好题啊~~(于是我爆0了) 注意到一个重要的性质就是期望是线性的,也就是说每一段的期望步数可以直接加起来,那么dp求出每一段的期望就行了... 设$f_i$表示从$i$出发不回到$i$ ...
- 【2017 北京集训 String 改编版】子串
题意 你有一个字符串,你需要支持两种操作: 1:在字符串的末尾插入一个字符 \(c\) 2:询问当前字符串的 \([l,r]\) 子串中的不同子串个数 为了加大难度,操作会被加密(强制在线). \(n ...
- (2016北京集训十)【xsy1530】小Q与内存
一道很有意思的神题~ 暴力平衡树的复杂度很对(并不),但是$2^{30}$的空间一脸屎 这题的正解是一个类似线段树的数据结构,我觉得很有创新性Orz 首先可以想到一种暴力就是用一个点代表一个区间,然后 ...
随机推荐
- 在ASP.NET MVC中使用Area区域
在大型的ASP.NET mvc5项目中一般都有许多个功能模块,这些功能模块可以用Area(中文翻译为区域)把它们分离开来,比如:Admin,Customer,Bill.ASP.NET MVC项目中把各 ...
- Java 学习笔记提高篇
Java笔记(提高篇)整理 主要内容: 面向对象 异常 数组 常用类 集合 IO流 线程 反射 Socket编程 1. 面向对象 1.1包 用来管理Java中的类, 类似文件夹管理文件一样. 因 ...
- Chrome浏览器 调试工具 vue-devtools 的安装和使用
https://www.cnblogs.com/yuqing6/p/7440549.html
- 如何配置Java环境变量[转]
https://jingyan.baidu.com/article/fd8044fa2c22f15031137a2a.html
- Java16-java语法基础——异常
Java16-java语法基础——异常 一.异常概念 1.异常:应用程序在运行过程中出现的错误或非正常的意外情况,即虚拟机的通常操作中可能遇到的异常,是一种常见的运行错误. 2.原因:数组越界.空指针 ...
- Python3实战系列之六(获取印度售后数据项目)
问题:续接上一篇.说干咱就干呀,勤勤恳恳写程序呀! 目标:此篇我们试着把python程序打包成.exe程序.这样就可以在服务器上运行了.实现首篇计划列表功能模块的第三步: 3..exe文件能在服务器上 ...
- 黑马java课程2222
课程叫做27天学通java零基础 java 安装: 必须装jdk-7u72-windows-i586.exe 注意必须安装32位的就是i586这个.因为x64的不向下兼容.会有意向不到的bug 配置P ...
- Java多线程系列1 线程创建以及状态切换
我们知道线程线程有三种创建方式 1实现Runnable接口 2 继承Thread类 3使用Callable和Future接口创建线程.具体是创建Callable接口的实现类,并实现clall()方法. ...
- Python中添加中文注释报错SyntaxError: Non-UTF-8 code starting with '\xc1'
问题:在文本编辑器中编辑Python文件时添加中文注释,运行python文件时报错.SyntaxError: Non-UTF-8 code starting with '\xc1' 解决方法:在文本开 ...
- Linux安装yum
方法/步骤 1 查看.卸载已安装的yum包 查看已安装的yum包 #rpm –qa|grep yum 卸载软件包 #rpm –e –nodeps yum 2 下载安装依赖包python python- ...