luogu P3500 [POI2010]TES-Intelligence Test
题面传送门
题目描述:给定一个标准串\(a\),再给出很多串\(b\),问\(b\)是不是\(a\)的子序列。
方法一:最长公共子序列(太无脑了)
方法二:首尾一个一个对过去比较
方法三:链表
对于每个\(a_i\),令\(s_i\)指向下一个与\(a_i\)值相同的位置。
我们在匹配子序列时,对于一个\(b_i\),原本我们要一个个去比较,现在我们只要\(O(1)\)就可以指向所对应的元素。并查询,如果不满足上升的要求,便往后一个。直到找到第一个满足上升要求的,并使其与\(b_i\)匹配。这个贪心的思路大家一想就通。
代码实现:
#include<cstdio>
#include<cstring>
using namespace std;
int f[1000039],a[1000039],b[1000039],s[1000039],x[1000039],y,z,n,m,head,k,h[1000039],flag;
int main() {
register int i;
scanf("%d",&n);
for(i=1; i<=n; i++) {
scanf("%d",&a[i]);
if(!f[a[i]]) f[a[i]]=i;
if(!s[a[i]]) s[a[i]]=i;
else b[s[a[i]]]=i,s[a[i]]=i;
}
for(i=1; i<=1000000; i++) h[i]=f[i];
scanf("%d",&m);
while(m--) {
head=flag=0;
scanf("%d",&k);
for(i=1; i<=k; i++)scanf("%d",&x[i]);
for(i=1; i<=k; i++) {
while(h[x[i]]&&h[x[i]]<head) h[x[i]]=b[h[x[i]]];
if(!h[x[i]]) {flag=1;break;}
else head=h[x[i]],h[x[i]]=b[h[x[i]]];
}
for(i=1; i<=k; i++) h[x[i]]=f[x[i]];
if(flag) printf("NIE\n");
else printf("TAK\n");
}
}
本来以为能\(AC\)的,但只有\(78\)分,不愧评上了蓝题。我们把这个模型再抽象,抽象到有\(a_i\)个队列,里面的元素在逐个检查。这可以用\(vector\)来实现。不过我们可以在入队时使其具有单调性,这样到后面就可以二分查找第一个大于这个元素的值。考试时以为第一种方法如果数据随机和二分复杂度差不多,所以没打二分,也觉得二分麻烦。考试后打了一下,没想到比解法三还好打。
代码实现:
#include<cstdio>
#include<vector>
using namespace std;
int n,m,a[1000039],k,x[1000039],l,r,mid,head,h[1000039],flag;
vector<int> f[1000039];
inline void read(int &x){
x=0;char s=getchar();
while(s<'0'||s>'9')s=getchar();
while(s>='0'&&s<='9')x=(x<<3)+(x<<1)+(s^48),s=getchar();
}
int main(){
register int i;
scanf("%d",&n);
for(i=1;i<=n;i++) read(a[i]),f[a[i]].push_back(i);
scanf("%d",&m);
while(m--){
flag=head=0;
read(k);
for(i=1;i<=k;i++) read(x[i]);
for(i=1;i<=k;i++){
l=-1;r=f[x[i]].size();
//printf("%d\n",r);
if(!r) {flag=1;break;}
while(l+1<r){
mid=(l+r)>>1;
if(f[x[i]][mid]>head) r=mid;
else l=mid;
}
//printf("%d\n",r);
if(r==f[x[i]].size()) {flag=1;break;}
head=f[x[i]][r],h[x[i]]=r+1;
}
if(flag) printf("NIE\n");
else printf("TAK\n");
}
}
做这道题要有对二分足够的认识---\(wcx\)