【BZOJ 2806】 2806: [Ctsc2012]Cheat (SAM+二分+DP+单调队列)

2806: [Ctsc2012]Cheat

Time Limit: 20 Sec  Memory Limit: 256 MB
Submit: 1262  Solved: 643

Description

Input

第一行两个整数N,M表示待检查的作文数量,和小强的标准作文库
的行数
接下来M行的01串,表示标准作文库
接下来N行的01串,表示N篇作文

Output

N行,每行一个整数,表示这篇作文的Lo 值。

Sample Input

1 2

10110

000001110

1011001100

Sample Output

4

HINT

输入文件不超过1100000字节

注意:题目有改动,可识别的长度不小于90%即可,而不是大于90%

Source

【分析】

  显然可以二分。

  然后就是判断。

  先用SAM找到i最长向左可以匹配的位置l,决策区间就是[i-l,i-lim],思考一下发现这个决策区间是不断向右移动的,就是类似滑动窗口?这个就可以用单调队列了。

  即f[i]=max{f[i-1],f[j]+j-i|i-l<=j<=i-lim}

  1 #include<cstdio>
  2 #include<cstdlib>
  3 #include<cstring>
  4 #include<iostream>
  5 #include<algorithm>
  6 using namespace std;
  7 #define Maxn 2500010
  8
  9 int mymax(int x,int y) {return x>y?x:y;}
 10
 11 struct node
 12 {
 13     int son[4],pre,step;
 14 }t[Maxn*2];
 15
 16 struct sam
 17 {
 18     int tot,last;
 19     void extend(int k)
 20     {
 21         int np=++tot,p=last;
 22         t[np].step=t[p].step+1;
 23         while(p&&!t[p].son[k])
 24         {
 25             t[p].son[k]=np;
 26             p=t[p].pre;
 27         }
 28         if(!p) t[np].pre=1;
 29         else
 30         {
 31             int q=t[p].son[k];
 32             if(t[q].step==t[p].step+1) t[np].pre=q;
 33             else
 34             {
 35                 int nq=++tot;
 36                 memcpy(t[nq].son,t[q].son,sizeof(t[nq].son));
 37                 t[nq].step=t[p].step+1;
 38                 t[nq].pre=t[q].pre;
 39                 t[q].pre=t[np].pre=nq;
 40                 while(p&&t[p].son[k]==q)
 41                 {
 42                     t[p].son[k]=nq;
 43                     p=t[p].pre;
 44                 }
 45             }
 46         }
 47         last=np;
 48     }
 49 }sam;
 50
 51 char s[Maxn];
 52
 53 int q[Maxn*2];
 54 int f[Maxn];
 55 bool check(int x,int ll)
 56 {
 57     int l=1,r=1;
 58     int nw=1,sm=0;
 59     for(int i=1;i<=ll;i++) f[i]=0;
 60     q[1]=0;
 61     for(int i=1;i<=ll;i++)
 62     {
 63         f[i]=f[i-1];
 64         int ind=s[i]-‘0‘;
 65         while(nw&&!t[nw].son[ind]) nw=t[nw].pre,sm=t[nw].step;
 66         if(t[nw].son[ind]) nw=t[nw].son[ind],sm++;
 67         else {nw=1;sm=0;continue;}
 68         while(i-x>=0&&l<=r&&f[i-x]-(i-x)>f[q[r]]-(q[r])) r--;
 69         if(i-x>=0) q[++r]=i-x;
 70         while(l<=r&&(q[l]<i-sm||q[l]>i-x)) l++;
 71         if(l<=r) f[i]=mymax(f[i],f[q[l]]+i-q[l]);
 72     }
 73     return f[ll]*10>=ll*9;
 74 }
 75
 76 int main()
 77 {
 78     int n,m;
 79     scanf("%d%d",&n,&m);
 80     sam.tot=sam.last=1;
 81     for(int i=1;i<=m;i++)
 82     {
 83         scanf("%s",s);
 84         int ll=strlen(s);
 85         for(int j=0;j<ll;j++) sam.extend(s[j]-‘0‘);
 86         // sam.extend(2);
 87         sam.last=1;
 88     }
 89     for(int i=1;i<=n;i++)
 90     {
 91         scanf("%s",s+1);
 92         int ll=strlen(s+1);
 93         int l=0,r=ll;
 94         while(l<r)
 95         {
 96             int mid=(l+r+1)>>1;
 97             if(check(mid,ll)) l=mid;
 98             else r=mid-1;
 99         }
100         printf("%d\n",l);
101     }
102     return 0;
103 }

【打的是广义SAM?

2017-04-18 12:55:48

时间: 2024-10-08 10:37:13

【BZOJ 2806】 2806: [Ctsc2012]Cheat (SAM+二分+DP+单调队列)的相关文章

bzoj 2806: [Ctsc2012]Cheat【广义SAM+二分+dp+单调队列】

把模板串建一个广义SAM 然后在线查询,每次在SAM上预处理出一个a[i]表示i位置向前最多能匹配多长的模板串 二分答案L,dp判断,设f[i]为·~i有几个匹配,转移显然是f[i]=max{f[i-1],f[j]+i-j(i-a[i]<=j<=i-L)},根据性质,i-a[i]是单调的(或者直接看a[i]的预处理过程也能看出来),所以这个dp可以用单调队列优化转移,最后判断是否f[n]>=L*0.9 #include<iostream> #include<cstdio

[bzoj2806][Ctsc2012]Cheat(后缀自动机(SAM)+二分答案+单调队列优化dp)

偷懒直接把bzoj的网页内容ctrlcv过来了 2806: [Ctsc2012]Cheat Time Limit: 20 Sec  Memory Limit: 256 MBSubmit: 1943  Solved: 1004[Submit][Status][Discuss] Description Input 第一行两个整数N,M表示待检查的作文数量,和小强的标准作文库的行数接下来M行的01串,表示标准作文库接下来N行的01串,表示N篇作文 Output N行,每行一个整数,表示这篇作文的Lo

复习【dp+单调队列】

处理10W数据:二分答案+单调队列+dp 这题可以用二维Dp和一维Dp来做,虽然两者时间复杂度都是O(N2),但二维的无法被优化,一维的可以用单调队列将时间效率缩为O(N).因此构造Dp时优先采用纬度小的方案. 单调队列的核心模板见代码. ps:在一部分单调队列的题目中,队列各项的数值会同时变化.此时应用变量Plus做维护 #include <iostream> using namespace std; struct qnode { long tim, x; }; const long N=1

习题:烽火传递(DP+单调队列)

烽火传递[题目描述]烽火台又称烽燧,是重要的防御设施,一般建在险要处或交通要道上.一旦有敌情发生,白天燃烧柴草,通过浓烟表达信息:夜晚燃烧干柴,以火光传递军情.在某两座城市之间有n个烽火台,每个烽火台发出信号都有一定的代价.为了使情报准确的传递,在m个烽火台中至少要有一个发出信号.现输入n.m和每个烽火台发出的信号的代价,请计算总共最少需要花费多少代价,才能使敌军来袭之时,情报能在这两座城市之间准确的传递!!![输入描述]第一行有两个数n,m(1<=n,m<=1000000)分别表示n个烽火台

poj3017 dp+单调队列

http://poj.org/problem?id=3017 Description Given an integer sequence { an } of length N, you are to cut the sequence into several parts every one of which is a consecutive subsequence of the original sequence. Every part must satisfy that the sum of

bzoj 2806: [Ctsc2012]Cheat 后缀自动机DP

2806: [Ctsc2012]Cheat Time Limit: 20 Sec  Memory Limit: 256 MBSubmit: 583  Solved: 330[Submit][Status][Discuss] Description Input 第一行两个整数N,M表示待检查的作文数量,和小强的标准作文库 的行数 接下来M行的01串,表示标准作文库 接下来N行的01串,表示N篇作文 Output N行,每行一个整数,表示这篇作文的Lo 值. Sample Input 1 2 101

P3957 跳房子[二分答案][dp][单调队列]

P3957 跳房子 前年pj没去年难好吧 首先要发现这个答案是有单调性的. 这个很显然了:氪金越多游戏越容易玩,氪金越少越难. 然而也有界限:如果所有正数的和加起来还不够需求,无解. 所以二分答案,考虑如何判定答案. 是人都知道要设一个\(dp[i]\)表示跳前\(i\)个房子的最大分数. 50pts就很简单的暴力转移: \[dp[i]=max(dp[j]+a[i]),j < i,lim_1<dis[i]-dis[j] < lim_2\] 其实很大的暗示了:把\(a[i]\)拿出来就是:

BZOJ 3316 JC loves Mkk 二分答案+单调队列

题目大意:给定一个环,要求在这个环上截取长度为偶数且在[L,R]区间内的一段,要求平均值最大 看到环果断倍增 看到平均值最大果断二分答案 看到长度[L,R]果断单调队列 对数组维护一个前缀和,对前缀和维护单调递增的单调队列 每扫过一个数sum[i],将sum[i-L]加入单调队列,再把距离i超过R的点删掉 长度为偶数?对奇数位置和偶数位置分别维护一个单调队列即可 每次找到大于0的子串之后记录一下分母再退出就行了 #include <cstdio> #include <cstring>

BZOJ 2500 幸福的道路 树形DP+单调队列

题目大意:给定一棵树,令a[i]为从第i个节点出发的最长链,求a[i]中最长的区间,满足区间内最大值与最小值之差不超过m 读错题害死人,脑残害死人 求a[i]显然是树形DP 考虑从一个点出发的链可以从子节点走,也可以从父节点走 因此我们DP两次,第一次求出从子节点走的最长链,第二次求出从父节点走的最长链,两次取max就是答案 但是直接DP会有问题,因为从父节点走的最长链可能是从自己的子树出发的,这样就会走重 因此除记录从子节点出发的最长链外还要记录一个从另一个子节点出发的次长链,如果最长链长度相