1094: 等差区间
Time Limit:5000/3000 MS (Java/Others) Memory Limit:163840/131072 KB (Java/Others)
Total Submissions:843 Accepted:89
[Submit][Status][Discuss]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<<2]; int Gcd(int a,int b) { if(a==0||b==0) return 0; if(a<b) { int t=a; a=b; b=t; } if(a%b==0) return b; return Gcd(b,a%b); } int cha[N],loc[N*10],last[N]; void build(int l,int r,int rt) { tree[rt].maxn=tree[rt].minn=0; tree[rt].l=l; tree[rt].r=r; if(l==r) { tree[rt].maxn=num[l]; tree[rt].minn=num[l]; tree[rt].g=-1; tree[rt].lef=last[l]; return; } int mid=(l+r)>>1; build(lson); build(rson); tree[rt].maxn=max(tree[rt<<1].maxn,tree[rt<<1|1].maxn); tree[rt].minn=min(tree[rt<<1].minn,tree[rt<<1|1].minn); if(tree[rt<<1].l==tree[rt<<1].r&&tree[rt<<1|1].l==tree[rt<<1|1].r) //建树时求出所有子节点数大于1的结点的公差 tree[rt].g=abs(num[tree[rt<<1].r]-num[tree[rt<<1|1].l]); else if(tree[rt<<1|1].l==tree[rt<<1|1].r) tree[rt].g=Gcd(tree[rt<<1].g,abs(num[tree[rt<<1].r]-num[tree[rt<<1|1].l])); else tree[rt].g=Gcd(Gcd(tree[rt<<1].g,abs(num[tree[rt<<1].r]-num[tree[rt<<1|1].l])),tree[rt<<1|1].g); tree[rt].lef=max(tree[rt<<1].lef,tree[rt<<1|1].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)>>1; 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+1,R,rson); Res r3; r3.maxn=max(r1.maxn,r2.maxn); r3.minn=min(r1.minn,r2.minn); if(r1.g==-1&&r2.g==-1) //查询时需注意,若查到叶子结点,其公约数为-1,需特殊处理 r3.g=abs(num[mid]-num[mid+1]); else if(r1.g==-1) r3.g=Gcd(abs(num[mid]-num[mid+1]),r2.g); else if(r2.g==-1) r3.g=Gcd(abs(num[mid]-num[mid+1]),r1.g); else r3.g=Gcd(r1.g,Gcd(abs(num[tree[rt<<1].r]-num[tree[rt<<1|1].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,0,sizeof(loc)); for(int i=1; i<=n; i++) { scanf("%d",&num[i]); if(loc[num[i]]==0) last[i]=0; else last[i]=loc[num[i]]; loc[num[i]]=i; if(i>1) cha[i-1]=abs(num[i]-num[i-1]); } build(1,n,1); while(q--) { int ll,rr; scanf("%d%d",&ll,&rr); Res tm=query(ll,rr,1,n,1); 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 "); continue; } if(lef<ll&&(g*(rr-ll)==maxn-minn)) printf("Yes "); else printf("No "); } } return 0; }
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][18],dpMin[N][18],dpG[N][18],dpLeft[N][18],n,loc[N*10]; int Gcd(int a,int b) { if(a==0||b==0) return 0; if(a<b) { int tmp=a; a=b; b=tmp; } if(a%b==0) return b; return Gcd(b,a%b); } void RMQ() { for(int j=1; j<=17; j++) for(int i=1; i<=n; i++) if((1<<j)+i-1<=n) { dpMax[i][j]=max(dpMax[i][j-1],dpMax[i+(1<<j-1)][j-1]); dpMin[i][j]=min(dpMin[i][j-1],dpMin[i+(1<<j-1)][j-1]); dpLeft[i][j]=max(dpLeft[i][j-1],dpLeft[i+(1<<j-1)][j-1]); if(j==1) dpG[i][j]=abs(dpMin[i][0]-dpMin[i+1][0]); else dpG[i][j]=Gcd(dpG[i][j-1],Gcd(abs(dpMin[i+(1<<j-1)][0]-dpMin[i+(1<<j-1)-1][0]),dpG[i+(1<<j-1)][j-1])); } } int main() { //cout<<log2(0)<<endl; int q; while(scanf("%d%d",&n,&q)!=EOF) { memset(loc,0,sizeof(loc)); for(int i=1; i<=n; i++) { scanf("%d",&dpMin[i][0]); dpMax[i][0]=dpMin[i][0]; dpG[i][0]=-1; if(loc[dpMin[i][0]]==0) dpLeft[i][0]=0; else dpLeft[i][0]=loc[dpMin[i][0]]; loc[dpMin[i][0]]=i; } RMQ(); while(q--) { int l,r; scanf("%d%d",&l,&r); int k=log2(r-l+1); int maxn=max(dpMax[l][k],dpMax[r-(1<<k)+1][k]); int minn=min(dpMin[l][k],dpMin[r-(1<<k)+1][k]); int left=max(dpLeft[l][k],dpLeft[r-(1<<k)+1][k]); int g=abs(dpMin[l][0]-dpMin[r][0]); if(r-l>1) g=Gcd(g,Gcd(dpG[l][k],dpG[r-(1<<k)+1][k])); //cout<<maxn<<' '<<minn<<' '<<g<<' '<<left<<endl; if(maxn==minn) printf("Yes "); else { if(left<l&&maxn-minn==(r-l)*g) printf("Yes "); else printf("No "); } } } return 0; }