bzoj 2806: [Ctsc2012]Cheat

题目大意:

这题就是sam,再二分加上单调队列优化DP。。。

就是先用标准作文库先做一个sam,中间用2隔开就行了

然后对于每个串,就先放到sam里去匹配,找到最长可以匹配的长度v[i]

二分L,再dp, f[i]=max(f[k]-k)+i  i-v[i]<=k<=i-L  因为i-v[i]与i-L都是单增的,所以可以用单调队列优化,最后判断是否f[n]是否大于等于n*0.9就行了


#include<cmath>
#include<cstdio>
#include<cstring>
#include<cassert>
#include<iostream>
#include<algorithm>
using namespace std;
const double eps=1e-8;
const int maxn=1100011;
int m,n;
void init(){scanf("%*d%d",&m);}
int v[maxn];
bool check(int limit){
    static int q[maxn],head,tail,f[maxn];
    q[head=1]=tail=0; f[0]=0;
    for (int i=1,tmp;i<=n;++i){
        f[i]=f[i-1];
        if ((tmp=i-limit)>=0){
            while (head<=tail && f[tmp]-tmp>=f[q[tail]]-q[tail]) --tail;
            q[++tail]=tmp;
        }
        while (head<=tail && i-v[i]>q[head]) ++head;
        if (head<=tail) f[i]=max(f[i],f[q[head]]+i-q[head]);
    }
    return f[n]>=n*0.9-eps;
}
struct Tsam{
    struct Tnode{
        Tnode *ch[3],*f; int ml;
        void clear(){memset(ch,0,sizeof(0)); f=NULL; ml=0;}
    }e[maxn<<1],*root,*last;
    int tot;
    Tnode *newnode(){e[tot].clear(); return e+(tot++);}
    void clear(){tot=0; root=last=newnode();}
    void add(int c){
        Tnode *np=newnode(),*p=last; np->ml=p->ml+1;
        for (last=np;p && !p->ch[c];p=p->f) p->ch[c]=np;
        if (!p) return np->f=root,void();
        Tnode *q=p->ch[c];
        if (q->ml==p->ml+1) return np->f=q,void();
        Tnode *r=newnode(); *r=*q; r->ml=p->ml+1;
        for (q->f=np->f=r;p && p->ch[c]==q;p=p->f) p->ch[c]=r;
    }
}sam;
char s[maxn];
void work(){
    for (sam.clear();m;m--){
        sam.add(2); scanf("%s",s);
        for (int i=0;s[i];++i) sam.add(s[i]-'0'); 
    }
    while (scanf("%s",s+1)!=EOF){
        n=strlen(s+1); Tsam::Tnode *p=sam.root;
        for (int i=1,len=0,c;i<=n;++i){
            c=s[i]-'0';
            if (p->ch[c]) p=p->ch[c],++len;
            else{
                while (p && !p->ch[c]) p=p->f;
                if (!p) p=sam.root,len=0;
                else len=p->ml+1,p=p->ch[c];
            }
            v[i]=len;
        }
        int t=1,w=n,mid;
        while (t<=w){
            mid=(t+w)>>1;
            if (check(mid)) t=mid+1;
            else w=mid-1;
        }
        printf("%d\n",t-1);
    }
}
int main(){
    init();
    work();
    return 0;
}
切过一道CTSC的题就是爽啊,果然还是太弱了。。。

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值