dutacm.club_1094_等差区间_(线段树)(RMQ算法)
1094: 等差区间
Total Submissions:843 Accepted:89
Description
已知一个长度为 nn 的数组 a[1],a[2],…,a[n],我们进行 qq 次询问,每次询问区间 a[l],a[l+1],…,a[r−1],a[r],数字从小到大排列后,是否会形成等差数列。等差数列的定义为,数列相邻两项(后一项减去前一项)的差值相等。
Input
本题有多组输入数据。
每组输入数据第一行输入两个正整数 nn 和 qq。第二行输入 nn 个正整数 a[1],a[2],…,a[n]。最后输入 qq 行,每行两个数字 l,rl,r(1≤l≤r≤n),表示询问区间 a[l],…,a[r]。
1≤n,q≤10^5,1≤a[i]≤10^6
Output
对于每组询问输出一行,如果形成等差数列,输出“Yes ”,否则输出“No”(不含引号)。
Sample Input
- 5 5
- 3 1 5 2 4
- 1 3
- 4 5
- 1 4
- 3 4
- 2 2
Sample Output
- Yes
- Yes
- No
- Yes
- Yes
- 题意:给定一个n位数列,q条查询[l,r],询问子序列[l--r]排序后是否为等差数列。
- 看题解说用的什么 RMQ求区间最大最小 但是没有学过,过些天再补。我用线段树来代替的求最大最小值。
- 思路:一个区间要是等差数列:1.所有数相等;2.所有数不等,且求公差,满足g*(r-l)==maxn-minn。
- 然后就是公差,我反正是没想到。求这个序列所有相邻两项差的最大公约数,结果即为公差,求公差也需要在线段树中进行,不能枚举。
- 同时,需要记录当前这个数上一次出现的位置。
- 线段树中维护区间:最大值,最小值,公差,序列中所有出现过的数上一次出现的位置的最大值(因为第2中情况需要所有数不同)
- #include<iostream>
- #include<cstdio>
- #include<cstring>
- #include<stdlib.h>
- using namespace std;
- #define lson l,mid,rt<<1
- #define rson mid+1,r,rt<<1|1
- #define N 100005
- int num[N];
- struct Node
- {
- int l,r;
- int maxn,minn,g,lef;
- } tree[N<<];
- int Gcd(int a,int b)
- {
- if(a==||b==)
- return ;
- if(a<b)
- {
- int t=a;
- a=b;
- b=t;
- }
- if(a%b==)
- return b;
- return Gcd(b,a%b);
- }
- int cha[N],loc[N*],last[N];
- void build(int l,int r,int rt)
- {
- tree[rt].maxn=tree[rt].minn=;
- tree[rt].l=l;
- tree[rt].r=r;
- if(l==r)
- {
- tree[rt].maxn=num[l];
- tree[rt].minn=num[l];
- tree[rt].g=-;
- tree[rt].lef=last[l];
- return;
- }
- int mid=(l+r)>>;
- build(lson);
- build(rson);
- tree[rt].maxn=max(tree[rt<<].maxn,tree[rt<<|].maxn);
- tree[rt].minn=min(tree[rt<<].minn,tree[rt<<|].minn);
- if(tree[rt<<].l==tree[rt<<].r&&tree[rt<<|].l==tree[rt<<|].r) //建树时求出所有子节点数大于1的结点的公差
- tree[rt].g=abs(num[tree[rt<<].r]-num[tree[rt<<|].l]);
- else if(tree[rt<<|].l==tree[rt<<|].r)
- tree[rt].g=Gcd(tree[rt<<].g,abs(num[tree[rt<<].r]-num[tree[rt<<|].l]));
- else
- tree[rt].g=Gcd(Gcd(tree[rt<<].g,abs(num[tree[rt<<].r]-num[tree[rt<<|].l])),tree[rt<<|].g);
- tree[rt].lef=max(tree[rt<<].lef,tree[rt<<|].lef);
- }
- struct Res
- {
- int maxn,minn,g,lef;
- Res(){}
- Res(int a,int b,int g1,int le)
- {
- maxn=a;
- minn=b;
- g=g1;
- lef=le;
- }
- };
- /*Res deal(Res a,Res b)
- {
- Res tmp(max(a.maxn,b.maxn),min(a.minn,b.minn));
- return tmp;
- }*/
- Res query(int L,int R,int l,int r,int rt)
- {
- if(L==l&&r==R)
- {
- Res tmp(tree[rt].maxn,tree[rt].minn,tree[rt].g,tree[rt].lef);
- return tmp;
- }
- int mid=(l+r)>>;
- if(L>mid)
- return query(L,R,rson);
- else if(R<=mid)
- return query(L,R,lson);
- else
- {
- Res r1=query(L,mid,lson);
- Res r2=query(mid+,R,rson);
- Res r3;
- r3.maxn=max(r1.maxn,r2.maxn);
- r3.minn=min(r1.minn,r2.minn);
- if(r1.g==-&&r2.g==-) //查询时需注意,若查到叶子结点,其公约数为-1,需特殊处理
- r3.g=abs(num[mid]-num[mid+]);
- else if(r1.g==-)
- r3.g=Gcd(abs(num[mid]-num[mid+]),r2.g);
- else if(r2.g==-)
- r3.g=Gcd(abs(num[mid]-num[mid+]),r1.g);
- else
- r3.g=Gcd(r1.g,Gcd(abs(num[tree[rt<<].r]-num[tree[rt<<|].l]),r2.g));
- r3.lef=max(r1.lef,r2.lef);
- return r3;
- }
- }
- int main()
- {
- int n,q;
- while(scanf("%d%d",&n,&q)!=EOF)
- {
- //memset(tree,0,sizeof(tree));
- memset(loc,,sizeof(loc));
- for(int i=; i<=n; i++)
- {
- scanf("%d",&num[i]);
- if(loc[num[i]]==)
- last[i]=;
- else
- last[i]=loc[num[i]];
- loc[num[i]]=i;
- if(i>)
- cha[i-]=abs(num[i]-num[i-]);
- }
- build(,n,);
- while(q--)
- {
- int ll,rr;
- scanf("%d%d",&ll,&rr);
- Res tm=query(ll,rr,,n,);
- int maxn=tm.maxn;
- int minn=tm.minn;
- int g=tm.g;
- int lef=tm.lef;
- //cout<<maxn<<' '<<minn<<' '<<g<<' '<<lef<<endl;
- if(minn==maxn)
- {
- printf("Yes\n");
- continue;
- }
- if(lef<ll&&(g*(rr-ll)==maxn-minn))
- printf("Yes\n");
- else
- printf("No\n");
- }
- }
- return ;
- }
- RMQ(Range Minimum/Maximum Query),即区间最值查询。一种动态规划。思路和线段树的一样。
- #include<iostream>
- #include<cstring>
- #include<cstdio>
- #include<cmath>
- #include<stdlib.h>
- using namespace std;
- #define N 100005
- int dpMax[N][],dpMin[N][],dpG[N][],dpLeft[N][],n,loc[N*];
- int Gcd(int a,int b)
- {
- if(a==||b==)
- return ;
- if(a<b)
- {
- int tmp=a;
- a=b;
- b=tmp;
- }
- if(a%b==)
- return b;
- return Gcd(b,a%b);
- }
- void RMQ()
- {
- for(int j=; j<=; j++)
- for(int i=; i<=n; i++)
- if((<<j)+i-<=n)
- {
- dpMax[i][j]=max(dpMax[i][j-],dpMax[i+(<<j-)][j-]);
- dpMin[i][j]=min(dpMin[i][j-],dpMin[i+(<<j-)][j-]);
- dpLeft[i][j]=max(dpLeft[i][j-],dpLeft[i+(<<j-)][j-]);
- if(j==)
- dpG[i][j]=abs(dpMin[i][]-dpMin[i+][]);
- else
- dpG[i][j]=Gcd(dpG[i][j-],Gcd(abs(dpMin[i+(<<j-)][]-dpMin[i+(<<j-)-][]),dpG[i+(<<j-)][j-]));
- }
- }
- int main()
- {
- //cout<<log2(0)<<endl;
- int q;
- while(scanf("%d%d",&n,&q)!=EOF)
- {
- memset(loc,,sizeof(loc));
- for(int i=; i<=n; i++)
- {
- scanf("%d",&dpMin[i][]);
- dpMax[i][]=dpMin[i][];
- dpG[i][]=-;
- if(loc[dpMin[i][]]==)
- dpLeft[i][]=;
- else
- dpLeft[i][]=loc[dpMin[i][]];
- loc[dpMin[i][]]=i;
- }
- RMQ();
- while(q--)
- {
- int l,r;
- scanf("%d%d",&l,&r);
- int k=log2(r-l+);
- int maxn=max(dpMax[l][k],dpMax[r-(<<k)+][k]);
- int minn=min(dpMin[l][k],dpMin[r-(<<k)+][k]);
- int left=max(dpLeft[l][k],dpLeft[r-(<<k)+][k]);
- int g=abs(dpMin[l][]-dpMin[r][]);
- if(r-l>)
- g=Gcd(g,Gcd(dpG[l][k],dpG[r-(<<k)+][k]));
- //cout<<maxn<<' '<<minn<<' '<<g<<' '<<left<<endl;
- if(maxn==minn)
- printf("Yes\n");
- else
- {
- if(left<l&&maxn-minn==(r-l)*g)
- printf("Yes\n");
- else
- printf("No\n");
- }
- }
- }
- return ;
- }
dutacm.club_1094_等差区间_(线段树)(RMQ算法)的更多相关文章
- BZOJ_4653_[Noi2016]区间_线段树+离散化+双指针
BZOJ_4653_[Noi2016]区间_线段树+离散化+双指针 Description 在数轴上有 n个闭区间 [l1,r1],[l2,r2],...,[ln,rn].现在要从中选出 m 个区间, ...
- BZOJ_2124_等差子序列_线段树+Hash
BZOJ_2124_等差子序列_线段树+Hash Description 给一个1到N的排列{Ai},询问是否存在1<=p1<p2<p3<p4<p5<…<pL ...
- CSU 1809 - Parenthesis - [前缀和+维护区间最小值][线段树/RMQ]
题目链接:http://acm.csu.edu.cn/csuoj/problemset/problem?pid=1809 Bobo has a balanced parenthesis sequenc ...
- [bzoj2124]等差子序列_线段树_hash
等差子序列 bzoj-2124 题目大意:给定一个1~n的排列,问是否存在3个及以上的位置上的数构成连续的等差子序列. 注释:$1\le n\le 10^4$. 想法:这题就相当于是否存在3个数i,j ...
- BZOJ_4636_蒟蒻的数列_线段树+动态开点
BZOJ_4636_蒟蒻的数列_线段树+动态开点 Description 蒟蒻DCrusher不仅喜欢玩扑克,还喜欢研究数列 题目描述 DCrusher有一个数列,初始值均为0,他进行N次操作,每次将 ...
- BZOJ_3252_攻略_线段树+dfs序
BZOJ_3252_攻略_线段树+dfs序 Description 题目简述:树版[k取方格数] 众所周知,桂木桂马是攻略之神,开启攻略之神模式后,他可以同时攻略k部游戏.今天他得到了一款新游戏< ...
- BZOJ_1826_[JSOI2010]缓存交换 _线段树+贪心
BZOJ_1826_[JSOI2010]缓存交换 _线段树+贪心 Description 在计算机中,CPU只能和高速缓存Cache直接交换数据.当所需的内存单元不在Cache中时,则需要从主存里把数 ...
- BZOJ_1828_[Usaco2010 Mar]balloc 农场分配_线段树
BZOJ_1828_[Usaco2010 Mar]balloc 农场分配_线段树 Description Input 第1行:两个用空格隔开的整数:N和M * 第2行到N+1行:第i+1行表示一个整数 ...
- BZOJ_1858_[Scoi2010]序列操作_线段树
BZOJ_1858_[Scoi2010]序列操作_线段树 Description lxhgww最近收到了一个01序列,序列里面包含了n个数,这些数要么是0,要么是1,现在对于这个序列有五种变换操作和询 ...
随机推荐
- C# .NET Visual Studio VS2008如何显示行号
工具-选项,然后勾选"显示所有设置",然后在文本编辑器下面找到所有语言,勾选"行号"即可.
- [Android Studio] 取消引用库打包出现异常-- provided dependencies can only be jars
Warning: Project App: provided dependencies can only be jars. com.android.support:appcompat-v7:22.2. ...
- HDU5465/BestCoder Round #56 (div.2) 二维树状数组
Clarke and puzzle 问题描述 克拉克是一名人格分裂患者.某一天,有两个克拉克(aa和bb)在玩一个方格游戏. 这个方格是一个n*mn∗m的矩阵,每个格子里有一个数c_{i, j}ci ...
- BZOJ 1037: [ZJOI2008]生日聚会Party 四维DP
1037: [ZJOI2008]生日聚会Party Time Limit: 10 Sec Memory Limit: 162 MBSubmit: 1650 Solved: 971[Submit][ ...
- 危险的input 微博的过去
更改uid post地址不变
- SQl 事物+视图+游标+索引+锁
一:事务: 是访问并可能更新数据库中各种数据项的一个程序执行单元(unit),事务是恢复和并发控制的基本单位. 事务的四个特性:ACID A:atomicity 原子性,事务里的所有操作都是一体的,要 ...
- html5 canvas程序演示--P1197 [JSOI2008]星球大战
html5 canvas程序演示--P1197 [JSOI2008]星球大战 <!doctype html> <html> <head> <meta char ...
- python 视频逐帧保存为图片
import cv2 import os def save_img(): video_path = r'F:\test\video1/' videos = os.listdir(video_path) ...
- openStack Aio 环境的neutron agent-list和cluster 环境 CLI结果对比
- HashMap1
一.Java并发基础 当一个对象或变量可以被多个线程共享的时候,就有可能使得程序的逻辑出现问题. 在一个对象中有一个变量i=0,有两个线程A,B都想对i加1,这个时候便有问题显现出来,关键就是对i加1 ...