Time Limit: 12000/4000 MS (Java/Others) Memory Limit: 65536/65536 K (Java/Others)
Total Submission(s): 1184 Accepted Submission(s): 134
Given a permutation P of 1 to N, YY wants to know whether there exists such three elements P[i 1], P[i 2], P[i 3] that P[i 1]-P[i 2]=P[i 2]-P[i 3], 1<=i 1<i 2<i 3<=N.
The first line is T(T<=60), representing the total test cases. Each test case comes two lines, the former one is N, 3<=N<=10000, the latter is a permutation of 1 to N.
For each test case, just output 'Y' if such i 1, i 2, i 3 can be found, else 'N'.
2 3 1 3 2 4 3 2 4 1
N Y
HNU
在1到N的排列中检查是否存在满足顺序等差的3个位数,由于题目时限的问题基本暴力还是能过的,我也是用的枚举的方法,时间复杂度是N2 ,跑了不到2S ,每个数字出现的位置,然后枚举Pi2,在Pi2-m与PI2+m之间查找是否存在2个数正好满足位置在Pi2的两侧,既a[j]-pos)*(a[2*i-j]-pos)<0;
#include <iostream>
#include <cstdio>
#include <cmath>
#include <cstring>
#include <string>
#include <cstdlib>
#include <vector>
#include <map>
#include <algorithm>
const int maxn = 10005;
bool vis1[maxn],vis2[maxn];
int a[maxn];
int n,i,j,k;
int main ()
{
int cas;
scanf("%d",&cas);
while (cas--)
{
int pos;
bool flag=false;
scanf("%d",&n);
for (i=0 ; i<n ; ++i)
{
scanf("%d",&pos);
a[pos]=i;
}
for (i=2 ; i<n; ++i)//emun i2
{
int m=((i-1)<(n-i)?(i-1):(n-i));
pos=a[i];
//printf("i=%d m=%d pos[i]=%d\n",i,m,pos);
for (j=i-m ; j<i ; ++j)
if((a[j]-pos)*(a[2*i-j]-pos)<0){/*printf("%d %d %d\n",j,i,2*i-j);*/flag=true;break;}
if(flag)break;
}
printf("%c\n",flag?'Y':'N');
}
return 0;
}
题解的方法:
(正解)
排列的性质就是1到N的数每个数都会出现,那么就是说,如果对于一个1到N的排列,如果前K个数里面没有X(1<=X<=N),那么后面的N-K个数里面一定会出现X。
等式A[p3]-A[p2]=A[p2]-A[p1]是通过数的位置的描述得出的,而我们发现可以通过从数本身的角度来描述上述等式。
定义Bi表示i这个数在原序列中出现的位置,那么对于一个数Y,需要判断的就变成了对于所有满足2Y=X+Z的X,Z,判断X,Z的出现的位置是否满足要求。
对于这个问题,我们要把它分解成两个小的问题,第一个就是找出所有满足要求的X,Z,第二个就是位置的出现有什么要求。
我们先来看第一个问题,因为2Y=X+Z,那么如果我们把1到N的数全部写在一个数轴上。
1 2 3 4 5 6 7 ……
假设Y=4,那么容易发现X=3,Z=5;X=2,Z=6;X=1,Z=7都是满足条件的对子
即满足条件的对子关于Y在数轴上的点轴对称。
现在我们再来看看第二个问题,位置有什么要求。
假设X,Z是关于Y的合法对子,那么Bx,Bz一定一个大于By,一个小于By。
现在我们尝试从另一个角度来描述这个问题。
从这个排列的第一个位置开始往后扫描,设每次扫到的数字为Aq,那么就判断所有满足条件的对子中是否存在一个在1到q中出现,一个在1到q中没有出现。
我们发现这个算法由于要判断这些对子,而这些对子的总数也是高达O(N2)的。
那么我们有什么方式可以快速判断多个对子呢?
前面的分析中用到的数轴,我们继续来用数轴来表示问题。一开始把数轴上1到N的位置都标记为0,表示暂时没有出现,然后每次向后扫描进入一个位置q,就将Aq上标记为1,并且同时判断对子是否存在一个出现,一个没有出现的。
举一个例子吧,比如{5,1,3,7,2,6,4}我们现在扫描到3了,那么我们来数轴上的情况。
1 2 3 4 5 6 7
1 01 0 1 0 0
我们要判断的对子就是(1,5),(2,4),也就是如果将这个东西看成01串,判断S(1->2),与S(5->4)是否相同。
判断连续的01字符串是否相等可以用Hash表来做,关于如何构造Hash值。
但是求Hash仍然是个O(N2),然后我们可以构造线段树来快速维护Hash(要两个线段树,一个维护正向的hash另一个逆向的hash值),这样我们可以通过一次扫描来判断,时间复杂度O(NlogN).
难度:★★★★
题解的代码:
还是没太看明白,我还是太菜了。。。
#include <iostream>
#include <cstdio>
using namespace std;
int n,m,test;
int f[10007],lt[100007],rt[100007],pr1,pr2;
long long h1[100007],h2[100007],x1,x2;
long long ch[10007];
bool fd;
void todo(int x,int y,int z)
{
lt[x]=y;rt[x]=z;h1[x]=h2[x]=0;
if (y<z)
{
todo(x<<1,y,(y+z)>>1);
todo((x<<1)+1,(y+z)/2+1,z);
}
}
void fd1(int x,int y,int z)
{
if (y<=lt[x] && rt[x]<=z)
{
int len=rt[x]-lt[x]+1;
x1=(((long long)x1*ch[len])+h1[x])%pr2;
}
else
if (rt[x]<y || z<lt[x]) return ;
else
{
fd1((x<<1)+1,y,z);
fd1(x<<1,y,z);
}
}
void fd2(int x,int y,int z)
{
if (y<=lt[x] && rt[x]<=z)
{
int len=rt[x]-lt[x]+1;
x2=(((long long)x2*ch[len])+h2[x])%pr2;
}
else
if (rt[x]<y || z<lt[x]) return ;
else
{
fd2(x<<1,y,z);
fd2((x<<1)+1,y,z);
}
}
void into(int x,int y)
{
if (lt[x]!=rt[x])
{
if (y<=(lt[x]+rt[x])/2) into(x<<1,y);
else into((x<<1)+1,y);
int len1=rt[(x<<1)]-lt[(x<<1)]+1;
h1[x]=(((long long)h1[(x<<1)+1])*ch[len1]+h1[x<<1])%pr2;
int len2=rt[(x<<1)+1]-lt[(x<<1)+1]+1;
h2[x]=(((long long)h2[x<<1])*ch[len2]+h2[(x<<1)+1])%pr2;
}
else
{
h1[x]=1;h2[x]=1;
}
}
int main()
{
double st = clock();
//freopen("YY's problem.in","r",stdin);
//freopen("YY's problem.out","w",stdout);
pr1=2317;pr2=999991;
ch[0]=1;
for (int i=1;i<=10000;i++) ch[i]=(((long long)ch[i-1])*pr1)%pr2;
for (cin>>test;test;test--)
{
cin>>n;fd=true;
for (int i=1;i<=n;i++) scanf("%d",&f[i]);
todo(1,1,n);
for (int i=1;i<=n;i++)
{
x1=x2=0;
if (f[i]!=1 && f[i]!=n)
if (n-f[i]>f[i]-1)
{
fd1(1,f[i]+1,f[i]*2-1);
fd2(1,1,f[i]-1);
if (x1!=x2) {fd=false;break;}
}
else
{
fd1(1,f[i]+1,n);
fd2(1,f[i]-(n-f[i]),f[i]-1);
if (x1!=x2) {fd=false;break;}
}
into(1,f[i]);
}
if (fd) printf("N\n");
else printf("Y\n");
}
cout<<" time = "<<clock() - st<<endl;
//fclose(stdin);
//fclose(stdout);
return 0;
}