[CTSC 2012][BZOJ 2806]Cheat

真是一道好题喵~

果然自动机什么的就是要和 dp 搞基才是王道有木有!

A:连 CTSC 都叫我们搞基,果然身为一个程序猿,加入 FFF 团是我此生最明智的选择。妹子什么闪边去,大家一起来搞基吧!

Q:教练你是什么时候产生了 dp 和自动机是同性的错觉~ 教练你又是什么时候产生了你还有个不入团的选择 ( 妹 子 )这样的错觉~

A:显而易见的,我们……

Q:教练不要转移话题啊!

A:显而易见的,我们先搞一个后缀自动机……

Q:等等,教练,多串的自动机要怎么写?

A:把几个串并在一起不就好了?

Q:不对啊,那查询的时候万一查到两个串相连的地方,不是悲剧了?

A:你傻啊!搞个分割符什么的不就解决了?!

Q:教练我的分割符是用 $ 还是用 # 呢?

(旁白:当然是 $ 了,$ 可是美金的意思,# 算什么,电话号码吗?)

A:。。。。。。

搞出了后缀自动机,就像我们经常做的一样,把每个节点能匹配的最长长度搞出来    (Q:我没文化你也不能这么逗我!)

把字串 S 在第 i 位能匹配到的最长长度 same[i] 搞出来

考虑二分答案L

就有了个显而易见的 dp 转移方程:

f[i]=max{f[j]-j+i | i-same[i]<=j<=i-L}

然后题目就被妥妥的解决了

Q:不对啊教练,这 dp 是 O(n2) 的!

f[i]=max{f[j]-j+i | i-same[i]<=j<=i-L}  ↔  f[i]-i=max{f[j]-j | i-same[i]<=j<=i-L}

A:这显然是单调的,优化优化就 O(n) 了

Q:教练求证明……

A:这个显然嘛,显然……要不然这题还怎么做额?

Q:教练……

A:烦死了,你打个表不就行了!

  1 #include <cstdio>
  2 #include <cstring>
  3 #include <cmath>
  4 const int sizeOfType=3;
  5 const int sizeOfString=1200021;
  6 const int sizeOfMemory=5200025;
  7
  8 inline void getstring(char * s)
  9 {
 10     register char ch;
 11     register int top=0;
 12     do ch=getchar(); while (ch<‘0‘ || ch>‘1‘);
 13     do s[top++]=ch, ch=getchar(); while (ch>=‘0‘ && ch<=‘1‘);
 14     s[top]=‘\0‘;
 15 }
 16
 17 struct deque
 18 {
 19     int l, r;
 20     int q[sizeOfString];
 21     inline deque():l(0), r(-1) {}
 22     inline void clear() {l=0, r=-1;}
 23     inline bool empty() {return l>r;}
 24     inline int front() {return q[l];}
 25     inline int back() {return q[r];}
 26     inline void push(int x) {q[++r]=x;}
 27     inline void pop_front() {++l;}
 28     inline void pop_back() {r--;}
 29 };
 30
 31 int N, M;
 32 int len;
 33 int same[sizeOfString];
 34 char str[sizeOfString];
 35 deque q;
 36 int f[sizeOfString], g[sizeOfString];
 37 inline int max(int x, int y) {return x>y?x:y;}
 38 inline void dp(int);
 39
 40 namespace suffixAutomaton
 41 {
 42     struct node
 43     {
 44         int step;
 45         node * fail;
 46         node * ch[sizeOfType];
 47     };
 48     node memory[sizeOfMemory], * port=memory;
 49     node * dfa, * last;
 50     inline node * newnode(node * t=NULL)
 51     {
 52         node * newt=port++;
 53         newt->step=0;
 54         if (t) newt->fail=t->fail, t->fail=newt, memcpy(newt->ch, t->ch, sizeof t->ch);
 55         else newt->fail=NULL, memset(newt->ch, 0, sizeof newt->ch);
 56         return newt;
 57     }
 58     inline void clear() {port=memory; dfa=newnode(); dfa->fail=dfa; last=dfa;}
 59
 60     inline int ord(char ch) {return ch>=‘0‘?ch-‘0‘:2;}
 61     inline void insert(int w)
 62     {
 63         node * p=last, * newp=newnode();
 64         newp->step=p->step+1;
 65
 66         for ( ;p->ch[w]==NULL;p=p->fail) p->ch[w]=newp;
 67         if (p->ch[w]==newp)
 68             newp->fail=dfa;
 69         else
 70         {
 71             node * q=p->ch[w];
 72             if (q->step==p->step+1)
 73                 newp->fail=q;
 74             else
 75             {
 76                 node * newq=newnode(q);
 77                 newq->step=p->step+1;
 78                 newp->fail=newq;
 79                 for ( ;p->ch[w]==q;p=p->fail) p->ch[w]=newq;
 80             }
 81         }
 82
 83         last=newp;
 84     }
 85     inline void buildDfa()
 86     {
 87         static char s[sizeOfString];
 88         int len;
 89
 90         clear();
 91         for (int i=0;i<M;i++)
 92         {
 93             getstring(s); len=strlen(s);
 94             for (int j=0;j<len;j++)
 95                 insert(ord(s[j]));
 96             insert(ord(‘$‘));
 97         }
 98     }
 99     inline void search(char * s)
100     {
101         int tot=0;
102         node * t=dfa;
103
104         for (int i=1;i<=len;i++)
105         {
106             int w=ord(s[i-1]);
107             if (t->ch[w])
108             {
109                 t=t->ch[w];
110                 ++tot;
111             }
112             else
113             {
114                 node * j;
115                 for (j=t->fail;j!=dfa && !j->ch[w];j=j->fail);
116                 if (j->ch[w])
117                 {
118                     t=j->ch[w];
119                     tot=j->step+1;
120                 }
121                 else
122                 {
123                     t=dfa;
124                     tot=0;
125                 }
126             }
127             same[i]=tot;
128         }
129     }
130 }
131
132 int main()
133 {
134     scanf("%d %d", &N, &M);
135     suffixAutomaton::buildDfa();
136     for (int i=1;i<=N;i++)
137     {
138         getstring(str);
139         len=strlen(str);
140         suffixAutomaton::search(str);
141
142         int L=0, R=len+1;
143         while (L+1<R)
144         {
145             int M=(L+R)>>1;
146             dp(M);
147             if (10*f[len]>=9*len) L=M;
148             else R=M;
149         }
150
151         printf("%d\n", L);
152     }
153
154     return 0;
155 }
156 inline void dp(int L)
157 {
158     q.clear();
159
160     for (int i=1;i<=len;i++)
161     {
162         int j=i-L;
163         if (j>=0)
164         {
165             for ( ;!q.empty() && g[j]>g[q.back()];q.pop_back());
166             q.push(j);
167         }
168         f[i]=f[i-1];
169         for ( ;!q.empty() && q.front()<i-same[i];q.pop_front());
170         if (!q.empty())
171             f[i]=max(f[i], g[q.front()]+i);
172         g[i]=f[i]-i;
173     }
174 }

本傻乱搞系列

后记:

话说我根本不知道如何证明单调性,但是大家注意到这是类似于一个一直在往右移动的窗口(不要问我为什么 i-same[i] 是单增的,事实如此)

移动的窗口,求窗口中最大的数……很眼熟啊!经验和直觉都告诉我们这就是单调队列……

大家理性理解一下就好,就好~

[CTSC 2012][BZOJ 2806]Cheat,布布扣,bubuko.com

时间: 2024-08-05 15:22:17

[CTSC 2012][BZOJ 2806]Cheat的相关文章

BZOJ 2806 cheat

首先这个题目显然是要二分转换成判断可行性的 之后我们考虑DP 设f(i)表示 1->i 熟悉的子串的长度的最大值 那么对于i这个点,要么不在熟悉的子串中,要么在熟悉的子串中 所以得到 f(i)=max(f(i-1),f(j)+i-j); 其中i-j是划分的熟悉的子串的长度,要满足以下条件: 1.i-j>=k (k为二分出来的值) 2.[j+1,i]这段串是给定标准文章库的一个子串 我们又知道若[j+1,i]是一个满足条件的子串,那么[j+2,i]也一定满足条件 假设我们已知最小的p满足[p+1

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

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

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

bzoj 2806: [Ctsc2012]Cheat

传送门 好久没刷bzoj惹-- 题意不说可以嘛. 首先二分答案. SAM的事情搞完以后就是dp辣. 我们已经对于每个位置i,找到了最小的一个k,使得[k,i]这个子串在模版串中出现过.那么我们需要做的是把f[i]给min上f[k]到f[i-x],直接搞是$n^2logn$的,套个数据结构也是两个log的.然而如果一个位置j不在合法的区间中,那么以后也不会进入,那么直接用一个单调队列维护就好了. #include<cstdio> #include<cstring> #include&

BZOJ 2806 [Ctsc2012]Cheat ——后缀自动机 单调队列优化DP

先建出广义后缀自动机. 然后跑出文章中每一个位置的最大匹配距离. 然后定义$f[i]$表示匹配到以$i$结尾的串时,最长的匹配距离. 显然可以二分$L$的取值. 然后容易得到$DP$方程 $f[i]=max(f[i-1],f[j]+i-j)(j<=i-L)$ 然后就发现$j$属于一个区间,然后就可以单调队列优化了. #include <map> #include <ctime> #include <cmath> #include <queue> #in

bzoj 2806: [Ctsc2012]Cheat 熟悉的文章

[题意] [题解] fhq自己写的题解就很清楚啦 (里面有关于神秘的阿米巴同学的介绍哦!很帅的样子,,, 我再来理一下思路 这道题思路应该是很简单很常规的,,但我做题太少辣! 二分一下肯定是必要的, 下面是判断是否可行的问题. 容易想到 要开一个 f 数组 表示 扫到了 第 i 位时 能匹配上多少个字母, 最后答案就是 f[lenth] * 10 >= lenth * 9 啦 f[i] 显然可以 通过 f[j] 转移 (j <= i - 二分出的下限的长度 + 1  且s[j……i]出现在字典

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

传送门 写CTSC的题真是爽啊.. 首先把所有库里的串连起来建一颗SAM.然后对于每个串都在SAM上跑一遍,记录下每个位置的最大匹配长度. 然后二分出L,用DP验证. 如果设$f[i]$表示第$i$个点所能匹配的最长的长度,那么很容易得到一个方程:$f[i]=max\{ f[j]+i-j\}$,限制条件为$i-Max[i] \leq j \leq i-L$. 这个方程是$O(N^2)$的 ,太暴力了,我们可以发现,$i-L$是递增的,所以考虑用单调队列优化. 每次加入一个点前,检查一下队尾,因为

【BZOJ 1148】【CTSC 2008】挂缀【BZOJ 1 148】【CTSC 2008】挂缀

[BZOJ 1 148][CTSC 2008]挂缀 这题显然是个贪心,然而我们应该如何贪才能得到最优解= =.... 假设我们按重量升序贪心,那我们可以得到反例(假设在挂缀底部):设有i,j,j<i且W_j<W_i 那么当C_i<W_j,W_i<C_j时,应该选i而不是j 假设我们按拉力升序贪心,依旧可以得到反例(假设在挂缀顶部,S为链以下重量和):设有i,j,j<i,且C_j<C_i 那么当C_i<S+W_j,S+W_i<C_j时,应该先选i当第二高的,而