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,现在对于这个序列有五种变换操作和询 ...
随机推荐
- AutoCAD如何编辑块,打散块
选中块之后,点击最右侧的最后一个工具"分解"即可.
- frameset怎样实现整个页面的跳转
登录页面login.jsp,系统登录成功后展示mainLayout.jsp, 我如今用frameset框架把页面mainLayout.jsp分为三部分,head.jsp..left.jsp.right ...
- 基于MFC的一个简单计算器
写一个简单的计算器并不是什么很难的事,主要目的是要通过这个程序来学习和分析其中的核心算法.这个简易计算器的核心部分就是对输入的表达式的正确性判断与求值,其中包括对表达式的解析.中缀表达式转后缀表达式. ...
- Fragment进阶(五)----->监听fragment回退事件
activity_main.xml <? xml version="1.0" encoding="utf-8"?> <LinearLayout ...
- ×变成x
昨天晚上遇到一个很尴尬的bug. 当使用IE浏览器,跳转链接使用&传参的时候第二个参数是times,也就是×(你有可能看到的是x,实际是×),结果& ...
- C#获取当前活动窗口句柄
c# 获取当前活动窗口句柄,获取窗口大小及位置 2018年04月26日 13:48:21 漂泊_人生 阅读数:1889 需调用API函数 需在开头引入命名空间using System.Runtim ...
- NHibernate之旅(8):巧用组件之依赖对象
本节内容 引入 方案1:直接加入 方案2:巧用组件 实例分析 结语 引入 通过前面7篇的学习,有点乏味了~~~这篇来学习一个技巧.大家一起想想假设我要在Customer类中实现一个Fullname属性 ...
- Android小技巧总结1
0.Android彻底退出引用程序的方法 1) Dalvik VM的本地方法 android.os.Process.killProcess(android.os.Process.myPid()); / ...
- Cocos2d-x项目创建
以创建HelloWorld项目为例子,执行create_project.py脚本,进入Doc界面输入下面的命令: (1)E: (切换盘符,因为我的Cocos2d-x源码在E盘,create_pro ...
- Codeforces Round #100 A. New Year Table
A. New Year Table time limit per test 2 seconds memory limit per test 256 megabytes input standard i ...