Bzoj2124 等差子序列
Time Limit: 3 Sec Memory Limit: 259 MB
Submit: 911 Solved: 337
Description
给一个1到N的排列{Ai},询问是否存在1<=p1=3),使得Ap1,Ap2,Ap3,…ApLen是一个等差序列。
Input
输入的第一行包含一个整数T,表示组数。下接T组数据,每组第一行一个整数N,每组第二行为一个1到N的排列,数字两两之间用空格隔开。
Output
对于每组数据,如果存在一个等差子序列,则输出一行“Y”,否则输出一行“N”。
Sample Input
23
1 3 2
3
3 2 1
Sample Output
NY
HINT
对于100%的数据,N<=10000,T<=7
表达能力不好,但还是试图解释一下。
有一种诡异的算法:
用二进制表示一个数是否已经选了,在某个位置的数若是a[i],若其向左数的hash不等于向右数的hash,说明一定有a[i]+x和a[i]-x分别在a[i]所在位置的两边,也就是有解。
↑上面这行目测看不懂,举个例子:
3
3 2 1 这是题目中的数据。我们用01串表示1、2、3这三个数选了没有,初始时是“0 0 0”
从左开始扫描,先选定3,此时01串表示为"0 0 1",3的位置在串最右(原数集中3最大),左右不可能有数和它构成等差序列。
然后选定2,此时01串表示为“0 1 1”,以2的位置为中心向左数,得到0;向右数,在对称位置得到1,这说明“比2小1的数”和“比2大1的数”一个选了(在左边)一个没选(在右边),那么它们就能构成等差序列了。
存储hash以判断01串是否相等,用线段树维护区间hash以方便查找,就大功告成了。
注意:由于是要判断一个位置左右“对称位置”是否不等,所以要存正序和逆序的hash。再举个例子:
有01串:0 1 0 1 [1] 0 1 1 0 1,从中心位置向左数得到的串是“1 0 1 0”,向右数得到的是“0 1 1 0”(两边长度要保持等于最短的一边的长度)。
#include<cstdio> #include<iostream> #include<algorithm> #include<cmath> #include<cstring> #define LL long long #define ls l,mid,rt<<1 #define rs mid+1,r,rt<<1|1 using namespace std; const int mod=1000000007; const int mxn=100000; int n; LL hashL[mxn],hashR[mxn],pw[mxn]; int a[mxn]; void pushup(int rt,int len){ LL tmp=len>>1; hashL[rt]=(hashL[rt<<1]*pw[tmp]+hashL[rt<<1|1])%mod;//更新左数hash hashR[rt]=(hashR[rt<<1|1]*pw[len-tmp]+hashR[rt<<1])%mod;//更新右数hash return; } void add(int x,int l,int r,int rt){ if(l==r){ hashL[rt]=hashR[rt]=1; return; } int mid=(l+r)>>1; if(x<=mid)add(x,ls); else add(x,rs); pushup(rt,r-l+1); } LL queryL(int L,int R,int l,int r,int rt){//查询hash if(L>R)return 0; if(L==l && r==R)return hashL[rt]; int mid=(l+r)>>1; if(L>mid)return queryL(L,R,rs); else if(R<=mid)return queryL(L,R,ls); else return (queryL(L,mid,ls)*pw[R-mid]+queryL(mid+1,R,rs))%mod; } LL queryR(int L,int R,int l,int r,int rt){ if(L>R)return 0; if(L==l && r==R)return hashR[rt]; int mid=(l+r)>>1; if(L>mid)return queryR(L,R,rs); else if(R<=mid)return queryR(L,R,ls); else return (queryR(L,mid,ls)+queryR(mid+1,R,rs)*pw[mid+1-L])%mod; } int main(){ pw[1]=3;//进制,大于等于2任选 for(int i=2;i<=mxn;i++)pw[i]=(pw[i-1]*3)%mod; int T; scanf("%d",&T); while(T--){ memset(hashL,0,sizeof hashL);//init memset(hashR,0,sizeof hashR); scanf("%d",&n); int i,j; for(i=1;i<=n;i++)scanf("%d",&a[i]); bool flag=0; for(i=1;i<=n;i++){ LL len=min(a[i]-1,n-a[i]); LL t1=queryL(a[i]-len,a[i]-1,1,n,1); LL t2=queryR(a[i]+1,a[i]+len,1,n,1); if(t1!=t2){//左右hash不等,则可以形成等差序列 flag=1; break; } add(a[i],1,n,1);//将该数加入hash } if(flag)printf("Y\n"); else printf("N\n"); } return 0; }
- Python之yield生成器
- NYOJ14会场安排问题
- C++基础之模板
- 不霁何虹 —— 彩虹为什么发生?
- Android动画之视图动画
- vijos1782:借教室
- Ubuntu redmine 安装
- Intellij IDEA:maven的本地仓库问题
- 【JZOJ 4639】Angel Beats!
- 剑指offer----二叉树的深度----java实现
- 疯牛&&青蛙过桥586
- CF-697B Barnicle与691C Exponential notation
- CSS Bug、CSS Hack 和 Filter的介绍
- 【图形学与游戏编程】开发笔记-入门篇2:一些需要的基础知识
- rapidjson解析数据
- jsp解决乱码问题。
- [可并堆] BZOJ 3011 [Usaco2012 Dec]Running Away From the Barn
- JVM 配置示例
- Jquery $.ajax/$.post/$.get
- 二进制中1的个数