给n<=1e6的序列每个数<=1e6,求m<=1e6个询问,每次问一个长度L<=n的序列是不是一开始给的序列的子序列。
贪心一波,每进来一个数就找他最早的可能位置,这样是比后面的位置要好的。一开始预处理,把每个数字的编号,按数字大小第一关键字,编号第二,放在一个表里,在表里面二分即可。
1 #include<stdio.h> 2 #include<string.h> 3 #include<stdlib.h> 4 #include<algorithm> 5 //#include<iostream> 6 using namespace std; 7 8 int n,m; 9 #define maxn 1000011 10 int a[maxn],ll[maxn],rr[maxn],list[maxn],cnt[maxn]; 11 int main() 12 { 13 scanf("%d",&n); 14 memset(cnt,0,sizeof(cnt)); 15 memset(ll,0,sizeof(ll));memset(rr,0,sizeof(rr)); 16 for (int i=1;i<=n;i++) scanf("%d",&a[i]),cnt[a[i]]++; 17 int now=1,Max=1e6+1; 18 for (int i=0;i<=Max;i++) if (cnt[i]) ll[i]=rr[i]=now,now+=cnt[i]; 19 for (int i=1;i<=n;i++) list[rr[a[i]]++]=i; 20 // for (int i=1;i<=n;i++) cout<<list[i]<<' ';cout<<endl; 21 scanf("%d",&m); 22 int b,x; 23 while (m--) 24 { 25 scanf("%d",&b); 26 now=1;bool flag=1; 27 while (b--) 28 { 29 // cout<<now<<' '; 30 scanf("%d",&x); 31 int L=ll[x],R=rr[x]; 32 while (L<R) 33 { 34 int mid=(L+R)>>1; 35 if (list[mid]>=now) R=mid; 36 else L=mid+1; 37 } 38 if (L==rr[x]) 39 { 40 flag=0; 41 while (b--) scanf("%d",&x); 42 break; 43 } 44 now=list[L]+1; 45 }//cout<<endl; 46 puts(flag?"TAK":"NIE"); 47 } 48 return 0; 49 }