luogu P3500 [POI2010]TES-Intelligence Test

题面传送门
题目描述:给定一个标准串 a a a,再给出很多串 b b b,问 b b b是不是 a a a的子序列。
方法一:最长公共子序列(太无脑了)
方法二:首尾一个一个对过去比较
方法三:链表
对于每个 a i a_i ai,令 s i s_i si指向下一个与 a i a_i ai值相同的位置。
我们在匹配子序列时,对于一个 b i b_i bi,原本我们要一个个去比较,现在我们只要 O ( 1 ) O(1) O(1)就可以指向所对应的元素。并查询,如果不满足上升的要求,便往后一个。直到找到第一个满足上升要求的,并使其与 b i b_i bi匹配。这个贪心的思路大家一想就通。
代码实现:

#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");
    }
}

本来以为能 A C AC AC的,但只有 78 78 78分,不愧评上了蓝题。我们把这个模型再抽象,抽象到有 a i a_i ai个队列,里面的元素在逐个检查。这可以用 v e c t o r vector 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");
    }
}

做这道题要有对二分足够的认识— w c x wcx wcx

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值