JSOI2009 等差数列 和 算术天才⑨与等差数列 和 CH4302 Interval GCD
等差数列
为了检验学生的掌握情况,jyy布置了一道习题:给定一个长度为N(1≤N≤100,000)的数列,初始时第i个数为vi(vi是整数,−100,000≤vi≤100,000),学生们要按照jyy的给出的操作步骤来改变数列中的某些项的值。操作步骤的具体形式为:A s t a b
(s,t,a,b均为整数,1≤s≤t≤N,−100,000≤a,b≤100,000),它表示,在序列的[s,t]区间上加上初值为a,步长为b的等差数列。即vi变为vi+a+b×(i−s)(对于s≤i≤t)。
在焦头烂额地计算之余,可怜的火星学生们还得随时回答jyy提出的问题。问题形式为:B s t
(s,t均为整数,1≤s≤t≤N),表示jyy询问当前序列的[s,t]区间最少能划分成几段,使得每一段都是等差数列。比如说1 2 3 5 7最少能划分成22段,一段是1 2 3,另一段是5 7。询问是需要同学们计算出答案后,作为作业交上来的。
虽然操作数加问题数总共只有Q(1≤Q≤100,000)个,jyy还是觉得这个题很无聊很麻烦。于是他想让你帮他算一份标准答案。
题解
推荐kskun的博客。
首先看到加等差数列就应该想到差分。令bi=ai+1-ai。
那么参考如下差分序列和原序列的对应
1 2 3 (4 4 4) 1 2 (3 3) 1 2
1 2 4 (7 11 15 19) 20 (22 25 28) 29 31
发现差分数列的连续相同值对应了原序列的等差数列。这部分显然应该分到一起。
至于那些零散值,一个就对应了长度为2的等差数列。经过讨论,我们得到如下简便算法:
- 零散段在区间两端,划分数+=区间长除以2上取整。
- 零散段在区间中间,划分数+=区间长除以2下取整。
线段树节点记下这个区间左右两端的零散值长度、左右两端点值(合并区间时检查左儿子的右端和右儿子的左端是否值相等,即并成一个等差数列)、除了零散值以外的那一段能够划分成最少多少等差数列、区间长度。
合并的时候讨论带合并的区间有没有纯零散值的,左右间隔处的情况即可。
时间复杂度 O(n log n)。
CO int N=100000+10;
int val[N];
struct node{
int siz,lval,rval;
int llen,rlen,div;
IN void add_tag(int v){
lval+=v,rval+=v;
}
};
node operator+(CO node&a,CO node&b){
node ans=(node){a.siz+b.siz,a.lval,b.rval};
ans.div=a.div+b.div;
bool flag=a.rval==b.lval;
if(a.div==0 and b.div==0){
if(flag) ans.llen=a.llen-1,ans.rlen=b.rlen-1,++ans.div;
else ans.llen=ans.rlen=ans.siz;
return ans;
}
else if(a.div==0){
ans.rlen=b.rlen;
if(flag){
ans.llen=a.llen-1;
if(b.llen) ans.div+=(b.llen-1)/2+1;
}
else ans.llen=a.siz+b.llen;
return ans;
}
else if(b.div==0){
ans.llen=a.llen;
if(flag){
ans.rlen=b.rlen-1;
if(a.rlen) ans.div+=(a.rlen-1)/2+1;
}
else ans.rlen=b.siz+a.rlen;
return ans;
}
ans.llen=a.llen,ans.rlen=b.rlen;
if(a.rlen==0 and b.llen==0){
if(flag) --ans.div;
return ans;
}
else if(a.rlen==0){
if(flag) ans.div+=(b.llen-1)/2;
else ans.div+=b.llen/2;
return ans;
}
else if(b.llen==0){
if(flag) ans.div+=(a.rlen-1)/2;
else ans.div+=a.rlen/2;
return ans;
}
int add=(a.rlen+b.llen)/2;
if(flag) add=min(add,(a.rlen-1)/2+1+(b.llen-1)/2);
ans.div+=add;
return ans;
}
node tree[4*N];
int tag[4*N];
#define lc (x<<1)
#define rc (x<<1|1)
#define mid ((l+r)>>1)
IN void push_down(int x){
if(tag[x]){
tree[lc].add_tag(tag[x]),tag[lc]+=tag[x];
tree[rc].add_tag(tag[x]),tag[rc]+=tag[x];
tag[x]=0;
}
}
void build(int x,int l,int r){
if(l==r){
tree[x]=(node){1,val[l],val[l],1,1,0};
return;
}
build(lc,l,mid),build(rc,mid+1,r);
tree[x]=tree[lc]+tree[rc];
}
void change(int x,int l,int r,int ql,int qr,int v){
if(ql<=l and r<=qr){
tree[x].add_tag(v),tag[x]+=v;
return;
}
push_down(x);
if(ql<=mid) change(lc,l,mid,ql,qr,v);
if(qr>mid) change(rc,mid+1,r,ql,qr,v);
tree[x]=tree[lc]+tree[rc];
}
node query(int x,int l,int r,int ql,int qr){
if(ql<=l and r<=qr) return tree[x];
push_down(x);
if(qr<=mid) return query(lc,l,mid,ql,qr);
if(ql>mid) return query(rc,mid+1,r,ql,qr);
return query(lc,l,mid,ql,qr)+query(rc,mid+1,r,ql,qr);
}
signed main(){
int n=read<int>();
for(int i=1;i<=n;++i) read(val[i]);
for(int i=1;i<=n;++i) val[i]=val[i+1]-val[i];
--n,build(1,1,n);
for(int q=read<int>();q--;){
char opt[2];scanf("%s",opt);
if(opt[0]=='A'){
int l=read<int>(),r=read<int>(),a=read<int>(),b=read<int>();
if(l>1) change(1,1,n,l-1,l-1,a);
if(r<=n) change(1,1,n,r,r,-(a+(r-l)*b));
if(l<r) change(1,1,n,l,r-1,b);
}
else{
int l=read<int>(),r=read<int>();
if(l==r){
puts("1");
continue;
}
node x=query(1,1,n,l,r-1);
int ans=(r-l+1+1)/2;
ans=min(ans,x.div+(x.llen+1)/2+(x.rlen+1)/2);
printf("%lld\n",ans);
}
}
return 0;
}
算术天才⑨与等差数列
给出一个长度为 N 的序列,需要支持以下两个操作:
- 单点修改
- 询问每个区间内数字升序排列后是否为等差数列·
N ≤ 100000, M ≤ 200000, datai ≤ 109
题解
若想知道区间内数是否能够成等差数列,显然要求出区间内的最值(即数列首项和末项),则即可算出对应的公差。
注意到若区间内数能成等差数列,则需满足下列中的一项:
- 公差为零;
- 公差不为零,则数列内无相同元素,那么公差为相邻的数值差的最大公因数。
于是需要维护,区间最大值最小值,差分数列的区间 gcd,以及每个数前驱相同元素的位置。维护每个数前驱相同元素的位置可以用 map 套 set。
可能是错的?
维护区间最大值,最小值,和,平方和,然后来判断。
等差数列平方和公式暴力展开就行了。
有人说能卡掉?
typedef __int128 LL;
CO int N=300000+10;
struct node{
int min,max;
LL sum1,sum2;
};
node operator+(CO node&a,CO node&b){
return (node){
min(a.min,b.min),max(a.max,b.max),
a.sum1+b.sum1,a.sum2+b.sum2
};
}
node tree[4*N];
#define lc (x<<1)
#define rc (x<<1|1)
void build(int x,int l,int r){
if(l==r){
int v=read<int>();
tree[x]=(node){v,v,v,(LL)v*v};
return;
}
int mid=(l+r)>>1;
build(lc,l,mid),build(rc,mid+1,r);
tree[x]=tree[lc]+tree[rc];
}
void change(int x,int l,int r,int p,int v){
if(l==r){
tree[x]=(node){v,v,v,(LL)v*v};
return;
}
int mid=(l+r)>>1;
if(p<=mid) change(lc,l,mid,p,v);
else change(rc,mid+1,r,p,v);
tree[x]=tree[lc]+tree[rc];
}
node query(int x,int l,int r,int ql,int qr){
if(ql<=l and r<=qr) return tree[x];
int mid=(l+r)>>1;
if(qr<=mid) return query(lc,l,mid,ql,qr);
if(ql>mid) return query(rc,mid+1,r,ql,qr);
return query(lc,l,mid,ql,qr)+query(rc,mid+1,r,ql,qr);
}
int main(){
int n=read<int>(),m=read<int>();
build(1,1,n);
int yes=0;
while(m--){
if(read<int>()==1){
int p=read<int>()^yes,v=read<int>()^yes;
change(1,1,n,p,v);
}
else{
int l=read<int>()^yes,r=read<int>()^yes,k=read<int>()^yes;
bool valid=1;
node x=query(1,1,n,l,r);
if(x.max-x.min!=(LL)(r-l)*k) valid=0;
if(x.sum1!=(LL)(x.min+x.max)*(r-l+1)/2) valid=0;
if(x.sum2!=(LL)(r-l+1)*x.min*x.min+(LL)2*k*(r-l)*(r-l+1)/2*x.min
+(LL)k*k*(r-l)*(r-l+1)*(2*r-2*l+1)/6) valid=0;
puts(valid?"Yes":"No"),yes+=valid;
}
}
return 0;
}
4302 Interval GCD 0x40「数据结构进阶」例题
描述
给定一个长度为N的数列A,以及M条指令 (N≤5*10^5, M<=10^5),每条指令可能是以下两种之一:
“C l r d”,表示把 A[l],A[l+1],…,A[r] 都加上 d。
“Q l r”,表示询问 A[l],A[l+1],…,A[r] 的最大公约数(GCD)。
输入格式
第一行两个整数N,M,第二行N个整数Ai,接下来M行每条指令的格式如题目描述所示。
输出格式
对于每个询问,输出一个整数表示答案。
样例输入
5 5
1 3 5 7 9
Q 1 5
C 1 5 1
Q 1 5
C 3 3 6
Q 2 4
样例输出
1
2
4
数据范围与约定
- N,M≤2*10^5,l<=r,数据保证任何时刻序列中的数都是不超过2^62-1的正整数。
</article>
分析
根据“更相减损术”,\(gcd(x,y,z)=gcd(x,y-x,z-y)\),所以
\]
所以用线段树维护差分序列的gcd,在线段树上单点修改区间查询;然后是要区间修改单点查询原序列的值,用树状数组维护新差分序列即可。
时间复杂度\(O(M \log N)\)
co int N=5e5+1;
int n,m;
ll a[N],b[N],c[N];
#define lowbit(i) (i&-i)
void add(int p,ll v){
for(;p<=n;p+=lowbit(p)) c[p]+=v;
}
ll ask(int p){
ll re=0;
for(;p;p-=lowbit(p)) re+=c[p];
return re;
}
ll s[N*4];
ll gcd(ll a,ll b) {return b?gcd(b,a%b):a;}
#define lc (x<<1)
#define rc (x<<1|1)
void build(int x,int l,int r){
if(l==r) return s[x]=b[l],void();
int mid=l+r>>1;
build(lc,l,mid),build(rc,mid+1,r);
s[x]=gcd(s[lc],s[rc]);
}
void change(int x,int l,int r,int p){
if(l==r) return s[x]=b[l],void();
int mid=l+r>>1;
if(p<=mid) change(lc,l,mid,p);
else change(rc,mid+1,r,p);
s[x]=gcd(s[lc],s[rc]);
}
ll query(int x,int l,int r,int ql,int qr){
if(ql>qr) return 0;
if(ql<=l&&r<=qr) return s[x];
int mid=l+r>>1;
if(qr<=mid) return query(lc,l,mid,ql,qr);
if(ql>mid) return query(rc,mid+1,r,ql,qr);
return gcd(query(lc,l,mid,ql,qr),query(rc,mid+1,r,ql,qr));
}
int main(){
// freopen("CH4302.in","r",stdin),freopen("CH4302.out","w",stdout);
read(n),read(m);
for(int i=1;i<=n;++i) b[i]=read(a[i])-a[i-1];
build(1,1,n); // edit 1: reserve for changing b[1]
for(int l,r;m--;){
static char op[2];
scanf("%s",op),read(l),read(r);
if(op[0]=='C'){
ll d=read<ll>(); // edit 2
b[l]+=d,change(1,1,n,l),add(l,d);
if(r+1<=n) b[r+1]-=d,change(1,1,n,r+1),add(r+1,-d);
}
else printf("%lld\n",llabs(gcd(a[l]+ask(l),query(1,1,n,l+1,r))));
}
return 0;
}
LG1438 无聊的数列
区间加等差数列和单点询问。
可以维护差分数组。也可以维护0次方和1次方和。
CO int N=1e5+10;
int sum[4*N],tag[4*N][2];
#define lc (x<<1)
#define rc (x<<1|1)
void push_up(int x){
sum[x]=sum[lc]+sum[rc];
}
void push_down(int x,int l,int r){
int mid=(l+r)>>1;
if(tag[x][0]){
sum[lc]+=(mid-l+1)*tag[x][0],tag[lc][0]+=tag[x][0];
sum[rc]+=(r-mid)*tag[x][0],tag[rc][0]+=tag[x][0];
tag[x][0]=0;
}
if(tag[x][1]){
sum[lc]+=(mid*(mid+1)/2-(l-1)*l/2)*tag[x][1],tag[lc][1]+=tag[x][1];
sum[rc]+=(r*(r+1)/2-mid*(mid+1)/2)*tag[x][1],tag[rc][1]+=tag[x][1];
tag[x][1]=0;
}
}
void build(int x,int l,int r){
tag[x][0]=tag[x][1]=0;
if(l==r) return read(sum[x]);
int mid=(l+r)>>1;
build(lc,l,mid),build(rc,mid+1,r);
push_up(x);
}
void change(int x,int l,int r,int ql,int qr,int v[2]){
if(ql<=l and r<=qr){
sum[x]+=(r-l+1)*v[0],tag[x][0]+=v[0];
sum[x]+=(r*(r+1)/2-(l-1)*l/2)*v[1],tag[x][1]+=v[1];
return;
}
push_down(x,l,r);
int mid=(l+r)>>1;
if(ql<=mid) change(lc,l,mid,ql,qr,v);
if(qr>mid) change(rc,mid+1,r,ql,qr,v);
push_up(x);
}
int query(int x,int l,int r,int ql,int qr){
if(ql<=l and r<=qr) return sum[x];
push_down(x,l,r);
int mid=(l+r)>>1;
if(qr<=mid) return query(lc,l,mid,ql,qr);
if(ql>mid) return query(rc,mid+1,r,ql,qr);
return query(lc,l,mid,ql,qr)+query(rc,mid+1,r,ql,qr);
}
#undef lc
#undef rc
signed main(){
int n=read<int>(),m=read<int>();
build(1,1,n);
while(m--){
if(read<int>()==1){
int l=read<int>(),r=read<int>();
int a=read<int>(),d=read<int>();
int v[2]={a-l*d,d};
change(1,1,n,l,r,v);
}
else{
int p=read<int>();
printf("%lld\n",query(1,1,n,p,p));
}
}
return 0;
}
JSOI2009 等差数列 和 算术天才⑨与等差数列 和 CH4302 Interval GCD的更多相关文章
- bzoj 4373 算术天才⑨与等差数列
4373: 算术天才⑨与等差数列 Time Limit: 10 Sec Memory Limit: 128 MBhttp://www.lydsy.com/JudgeOnline/problem.ph ...
- BZOJ4373 算术天才⑨与等差数列 【线段树】*
BZOJ4373 算术天才⑨与等差数列 Description 算术天才⑨非常喜欢和等差数列玩耍. 有一天,他给了你一个长度为n的序列,其中第i个数为a[i]. 他想考考你,每次他会给出询问l,r,k ...
- 【BZOJ4373】算术天才⑨与等差数列 [线段树]
算术天才⑨与等差数列 Time Limit: 10 Sec Memory Limit: 128 MB[Submit][Status][Discuss] Description 算术天才⑨非常喜欢和等 ...
- 【BZOJ4373】算术天才⑨与等差数列 线段树+set
[BZOJ4373]算术天才⑨与等差数列 Description 算术天才⑨非常喜欢和等差数列玩耍.有一天,他给了你一个长度为n的序列,其中第i个数为a[i].他想考考你,每次他会给出询问l,r,k, ...
- 【线段树 集合hash】bzoj4373: 算术天才⑨与等差数列
hash大法好(@ARZhu):大数相乘及时取模真的是件麻烦事情 Description 算术天才⑨非常喜欢和等差数列玩耍.有一天,他给了你一个长度为n的序列,其中第i个数为a[i].他想考考你,每次 ...
- [线段树]洛谷P5278 算术天才⑨与等差数列
题目描述 算术天才⑨非常喜欢和等差数列玩耍. 有一天,他给了你一个长度为n的序列,其中第i个数为a[i]. 他想考考你,每次他会给出询问l,r,k,问区间[l,r]内的数从小到大排序后能否形成公差为k ...
- BZOJ 4373: 算术天才⑨与等差数列 线段树
Description 算术天才⑨非常喜欢和等差数列玩耍. 有一天,他给了你一个长度为n的序列,其中第i个数为a[i]. 他想考考你,每次他会给出询问l,r,k,问区间[l,r]内的数从小到大排序后能 ...
- bzoj 4373 算术天才⑨与等差数列——线段树+set
题目:https://www.lydsy.com/JudgeOnline/problem.php?id=4373 能形成公差为k的等差数列的条件:mx-mn=k*(r-l) && 差分 ...
- bzoj 4373: 算术天才⑨与等差数列 hash
题目链接 题目大意: 给你n个数, 给两种操作, 一种给你l, r, k,问你[l, r]区间里的数排序后能否构成一个公差为k的等差数列. 另一种是将位置x的数变为y. 强制在线. 可以用hash来 ...
随机推荐
- Java注解及其原理以及分析spring注解解析源码
注解的定义 注解是那些插入到源代码中,使用其他工具可以对其进行处理的标签. 注解不会改变程序的编译方式:Java编译器对于包含注解和不包含注解的代码会生成相同的虚拟机指令. 在Java中,注解是被当做 ...
- golang---命令源码文件与命令行参数
命令源码文件是程序的运行入口,是每个可独立运行的程序必须拥有的. import "flag" flag包实现了命令行参数的解析.每个参数认为一条记录,根据实际进行定义,到一个se ...
- Java 加载动态库 dll 文件
不知道具体原理,但是,加载 dll 文件时,带路径或者更改 dll 文件的名字,都会报错.虽然库记载成功了,但是处女座认为这不可接受.于是有了这个解决方案. 在根目录为库创建软连接,然后使用 syst ...
- Vue,Javascript--时间戳的操作
new Date(parseInt(data.substring(6, data.length - 2))).toLocaleDateString(); 我这里的data记得替换成你的数据,在过滤器中 ...
- LeetCode-SQL(一)
1.组合两个表 表1: Person +-------------+---------+| 列名 | 类型 |+-------------+---------+| Person ...
- ubuntu开发常用收集
命令: 1.http://blog.csdn.net/simongeek/article/details/45271089 2.http://www.jianshu.com/p/654be9c0f13 ...
- docker run VS docker exec 的区别
“docker run”和“docker exec”都是 Docker 容器中用于执行的命令.然而,在不同的情况下,它们的使用有着本质上的区别. “docker run”命令 “docker run” ...
- ex_gcd求不定方程的最小正整数解
#include<bits/stdc++.h> using namespace std; int gcd(int a,int b) {return b?gcd(b,a%b):a;} int ...
- RocketMQ原理及源码解析
RocketMQ原理深入: 一.定义: RocketMQ是一款分布式.队列模型的消息中间件,有以下部分组成: 1.NameServer: 一个几乎无状态的节点,可集群部署,节点之间无任何信息同步 2. ...
- 在ZYNQ上裸机跑ARM程序的演示
今天给大家演示如何在ZYNQ上,裸机跑ARM程序,本测试用的是米尔Z-turn Board单板,测试代码用的XILINX官方的C语言测试程序,用于测试挂接在ARM总线上的设备是否正常,并在串口终端打印 ...