Since12-19(71)

------------------------------------进入字符串的世界^O^ ----------------------------------------------

2016-12-19 周一 (22)

▲19:28:46 BZOJ3670 动物园(NOI2014): KMP/变式/YY

      Y了一个下午没想到在BZOJ上水过去了^O^  ,最关键的一点->把不能重叠转化为能重叠的问题->对于i,只要找到最长的不重叠的子串h[i],既是前缀又是后缀,那么问题就转化成了求S[1...h[i]]内的能重叠的个数.直接可以递推得到.

      求h[i]还是用KMP 的方法,对于一个i,一直找j,若当前的j不能会发生重叠,再用j=pre[j]找到第一个不会重叠的位置借口.

for(j=0,i=2;i<=n;i++){
     while(j>0&&s[i]!=s[j+1])j=pre[j];
     if(s[i]==s[j+1])j++;
     while(j>i/2)j=pre[j];
     res=1ll*res*(f[j]+1)%P;
}

▲20:30:41 HDU1711 Number Sequence KMP模板题

▲20:38:31 HDU2087 剪花布条 KMP模板题

▲20:43:24 HDU2203 亲和串 循环移位判断包含->把串复制,但是注意首先判断两个串长度!!!

▲21:42:04 HDU4513 吉哥系列故事——完美队形II 贪心/Manacher


 

2016-12-20 周二 (25)

▲11:20:29 HDU4749 Parade Show KMP/YY? 两个串匹配,当且仅当串的数字的相对大小是一样的->对于每个下标i,在两个串中,比a[i]大的a[j]个数等与比b[i]大的b[j]个数,比a[i]小的a[j]个数等与比b[i]小的b[j]个数.为此,由于a,b的范围很小,可以直接预处理出每个权值的出现次数的前缀和就可以了.我直接维护每个数字a[i]前面比它大和比它小的极值确定对应的b[i]的范围,再KMP就可以了,这样是不受a,b范围限制的!!

for(i=1;i<=m;i++){
        if(!f[a[i]]){
            f[a[i]]=i,d[i]=0;
            it=st.upper_bound(a[i]);
            if(it!=st.end())R[a[i]]=*it;
            else R[a[i]]=K+1;//没有比自己大的 
            if(it!=st.begin()){it--;L[a[i]]=*it;}
            else L[a[i]]=0;
            st.insert(a[i]);
        }
        else d[i]=1;//d[i]=1说明i不是第一次出现 
    }
    f[0]=0;
    f[K+1]=K+1;
    for(i=2,j=0;i<=m;i++){
        while(j&&a[i]!=a[j+1])j=pre[j];
        if(a[i]==a[j+1])j++;
        pre[i]=j;
    }
    for(i=1,j=0,x=0;i<=n;i++){
        while(j&&!(x=chk(j+1,i)))j=pre[j];
        if(chk(j+1,i)){
            j++;
            f[a[j]]=b[i];
            if(j==m)j=0,res++;
        }
    }
View Code

▲16:10:27 HDU4333 Revolving Digits 扩展KMP/循环节判断

      1.扩展KMP用来求a串的每个后缀与b串的最长公共前缀.

      2.注意题目中求的是不同的数字个数,因此要去重.此题中去重就是要找到循环节,可以通过KMP,求出nxt[len],假如串存在循环节,那么循环节长度就是len-nxt[len]的倍数.

 ▲16:40:04 HDU3336 Count the String KMP/递推结论:dp[i]=dp[pre[i]]+1,dp[i]表示前缀i中包含的可重叠的前缀个数.


 

2016-12-21 周三 (27)

▲15:08:07 BZOJ3110【好题加亮】 K大数查询 (ZJOI2013) 整体二分+线段树//树套树:

      整体二分:解决答案在[L,R]的询问,通过求询问的答案和mid=(L+R)/2之间的关系再继续递归下去缩小LR范围.验证mid与答案的关系,就只要求出>mid的数的个数,而且是区间操作->线段树!!这样不用考虑插入的数的具体大小,只要考虑与mid的关系即可.

      线段树套线段树:外层线段树维护权值(和主席树一样),每个节点又维护一个线段树维护下标区间,实现区间更新/区间求和.实现时,只要给内层线段树分配节点编号,并且记录左右儿子的编号,并且仅当更新到某个节点时,再给他编号,没有编号的点的cnt值就默认为0.在树套树里,不确定内存要开多大,就在不超过内存限制的前提下,尽可能开大.

▲21:39:43 BZOJ 蚂蚁寻路(ZJOI2013) DP/前缀和优化:最关键的一点就是把最后围成图形看成是长短长短长短...长的相邻柱形,再把变量分离量化,再用前缀和最值/求和搞一下就好了.边界情况要注意,可以直接求出来. 


 

2016-12-22 周四(祝自己生日快乐啦啦啦~我17啦!) (28)

单纯形共度的一天...

▲21:14:09 UOJ179 线性规划 单纯形模板


 

2016-12-23 周五 (35)

▲10:02:40 BZOJ3112 防守战线(ZJOI2013) 单纯形/对偶性优化

▲10:59:27 BZOJ1061 志愿者招募(NOI2008) 单纯形/对偶性优化  与上题代码一模一样...

通过对偶性可以省去初始化环节,原理: (D),(L)互为对偶.

#include<cstdio>
#include<algorithm>
#include<ctime>
using namespace std;
const int M=50;
#define db double
const db eps=1e-9,oo=1e30;
db a[M][M];
int n,m,id[M],t[M],tp;
void Pivot(int x,int y){//交换x,y...x是非基,y是基 
    int i,j,p=t[x],q=t[y],b=y-n;
    db k=-a[b][x];
    swap(t[x],t[y]);
    swap(id[p],id[q]);
    for(i=0;i<=n;i++)a[b][i]/=k;
    a[b][x]=-1.0/k;
    for(j=0;j<=m;j++){
        if(j==b)continue;
        k=a[j][x];a[j][x]=0;
        if(k<eps&&k>-eps)continue;
        for(i=0;i<=n;i++)a[j][i]+=k*a[b][i];
    }
}
bool init(){
    int i,j,x,y;
    while(1){
        for(y=0,j=1;j<=m;j++){
            if(a[j][0]>-eps)continue;
            for(i=1,x=0;i<=n;i++)
                if(a[j][i]>eps){x=i;if(rand()%2)break;}
            if(!x)return false;
            Pivot(x,j+n);
            y=1;
        }
        if(!y)return true;
    }
}
int Simplex(){//单纯形 
    if(!init())return -1;//说明无解 
    int i,j,x,y;
    while(1){
        for(i=1,x=0;i<=n;i++){
            if(a[0][i]>eps){x=i;if(rand()%2)break;}
        }
        if(!x)return 1;
        db mn=oo;
        for(y=0,j=1;j<=m;j++){
            if(a[j][x]<-eps){
                db d=-a[j][0]/a[j][x];
                if(!y||d<mn)y=j,mn=d;
            }
        }
        if(!y)return 0;
        Pivot(x,y+n);
    }
}
代码实现
/*
第一步init 把负常数项转化为正的  通过把那一个约束条件的负系数非基变量与基变量交换 ,直到所有常数项都为正
若无法实现说明不可行...
 
然后得到了一组基础解 非基变量全部赋值为0,再得到基变量
找到目标值里一个系数为正的项k
假如没有说明已经是最优,就可以结束了.... 

再求出它最紧的约束:
a[j][0]-a[j][k]*x[k]=0
假如a[j][k]>0   ->x[k]=Min{a[j][0]/a[j][k]},记录对应的 y=j 
假如 x[k]没有限制-> Unbounded

找到了x和 y 把x,y转换过来.. 

转换(x,y):
1.转换t/id
2.找到第y个约束->  每一项的系数 -> /a[y][x]   第x项变为1/a[y][x] 
3.找到其他每个约束:
    每第i项的系数都要加上  -a[j][x]*a[y][i]
    对于目标值
        第i项的系数  加上a[0][x]*a[y][i] 
*/
单纯形具体思路

▲11:26:56 HDU1358 Period KMP/判断循环节

▲15:36:41 HDU3613 Best Reward 扩展KMP/枚举//Hash

▲19:27:32 HDU4125 Moles 二叉树/线段树/KMP

  注意set的内存不止4倍,而且速度非常,请谨慎使用.

▲21:58:28 HDU2222 Keywords Search AC自动机模板题

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<iostream>
using namespace std;
const int M=5e5+5;
int Q[M],pre[M][26],cnt[M],fail[M],n,m,tot=0;//1300w
char s[M*2],str[55];//
void Trie(){
    int i,k,cur=0,len=strlen(str);
    for(i=0;i<len;i++){
        k=str[i]-'a';
        if(!pre[cur][k])pre[cur][k]=++tot;
        cur=pre[cur][k];
    }
    cnt[cur]++;
}
void AC_auto(){
    int l=0,r=0,cur,i,nxt;
    for(i=0;i<26;i++)if(pre[0][i])Q[r++]=pre[0][i];
    while(l<r){
        cur=Q[l++];
        for(i=0;i<26;i++){
            if(pre[cur][i]){
                nxt=Q[r++]=pre[cur][i];
                fail[nxt]=pre[fail[cur]][i];
            }
            else pre[cur][i]=pre[fail[cur]][i];
        }
    }
}
void solve(){
    int i,j,k,cur,ans=0;
    tot=0;
    scanf("%d",&m);
    while(m--){
        scanf("%s",str);
        Trie();
    }
    AC_auto();
    scanf("%s",s+1);
    n=strlen(s+1);
    for(i=1,cur=0;i<=n;i++){
        k=s[i]-'a';
        while(pre[cur][k]==0&&cur!=0)cur=fail[cur];
        cur=pre[cur][k];
        k=cur;
        while(k&&~cnt[k]){
            ans+=cnt[k];
            cnt[k]=-1;
            k=fail[k];
        }
    }
    printf("%d\n",ans);
    for(i=0;i<=tot;i++){
        for(j=0;j<26;j++)pre[i][j]=0;
        fail[i]=cnt[i]=0;
    }
}
int main(){
    int cas;
    scanf("%d",&cas);
    while(cas--)solve();
    return 0;
}
AC自动机

▲22:24:11 HDU2896 病毒入侵 AC自动机模板/注意内存能开多大开多大.但不要爆掉..


 

2016-12-24 周六 (39)

▲08:41:15 HDU3065 病毒持续入侵 AC自动机

    小trick:①当输出具体包含的模板串编号,用正向表储存,补药用vector!!!!②多case注意清空数组!!(cnt/pre/head/fail)

▲09:28:06 HDU3407 String-Matching Automata AC自动机/KMP  一道英语能力比算法更重要的题目O__O "...

▲10:16:42 HDU3695 Computer Virus on Planet Pandora AC自动机

▲14:49:27 HDU2457/POJ3691 DNA repair KMP/AC自动机+DP 把与m个禁止串的最长前缀作为状态,最多有m*L个状态.再确定每个状态加一个字母后转为的新状态.


 

2016-12-27 周二(老爸生日快乐❤)(42)

▲13:50:14 POJ2778 DNA sequence AC自动机/矩阵快速幂 

        首先考虑这个问题:给定一个有向图,求从a点到b点走k步(可以重复经过某个点/边)的方案数.首先当k=1时,有一个初始的邻接矩阵A;假如k=2,C=∑A(a,i)*A(i,b);k=3,C=C*A...以此类推得到最后的矩阵 C=A^k.可以用快速幂进行优化.

▲17:01:29 HDU2243 考研路茫茫——单词情结 AC自动机/矩阵快速幂,求S=A+A2+A3+A4+....+Ak. 构造一个大矩阵.

▲22:29:57 hihoCoder 1455 【好题加亮】Rikka with Tree III 终态枚举/bitset/莫队

      直接枚举等差中项x,其中有两个情况:

      1)其中一个点在x的子树,只要维护x的子树即可

      2)剩下两个点都在x的子树里,这种情况直接做就不方便了.稍微转换一下思路:不论是1,2哪种情况,都只有一个点在x的某个儿子里,那么直接枚举一定在x的子树里的那个点所在的子树即可.那么每次维护一个子树t[L,R],相当于为t的fa寻找答案.

     维护[L,R]时采用莫队算法,先按左端点所在块排序,再按右端点排序


 

2016-12-28 周三 (43)

▲13:56:25 BZOJ 2343 【好题加亮】阿狸的打字机(NOI2011) AC自动机/DFS序/离线/BIT

    首先在trie树上构出所有串.有一个结论:若x字符串是以y代表的串的后缀,那么y经过若干次fail一定可以到x.

    那么问题就转化为了判断y到rt的每个节点是否可以经过若干次fail到x.因为每个节点的fail都是唯一确定的,所以可以把fail[c]作为fa[c]建立一棵树.那么节点t经过若干次fail到x,就可以转化为节点t是否在x的子树中.为了求在trie树上的点y到根的路径上的每个点对应的答案,可以将询问根据y排序,在bit中维护当前没有删除的字符,再通过dfs区间映射子树,把问题转为区间求和就ok了.

后缀数组我来了!!!


 

2016-12-29周四(44)

▲18:55:27 POJ2774 Long Long Message 后缀数组模板题 求最长公共子串  

 1 #include<cstdio>
 2 #include<cstring>
 3 #include<iostream>
 4 #include<algorithm>
 5 using namespace std;
 6 const int M=2e5+5;
 7 int sa[M],cnt[M],rnk[M],n,m,len,h[M],x[M],y[M],v[M];
 8 char s[M],str[M];
 9 int S=95;
10 void pt(int a[]){
11     for(int i=1;i<=n;i++)printf("%d ",a[i]);puts("");
12 }
13 bool cmp(int a,int b,int d){
14     if(rnk[a]!=rnk[b])return true;
15     int x,y;
16     x=(a+d>n)?0:rnk[a+d];
17     y=(b+d>n)?0:rnk[b+d];
18     return x!=y;
19 }
20 int ty(int x){
21     if(x<len)return 1;
22     if(x>len)return 2;
23     return 0;
24 }
25 void solve(){
26     int i,j,k,a,b,c,ans=0,t;
27     len=strlen(s+1);
28     m=strlen(str+1);
29     s[++len]='`';
30     for(i=1;i<=m;i++)s[i+len]=str[i];
31     n=len+m;
32     for(i=1;i<=n;i++)cnt[v[i]=s[i]-S]++;
33     for(i=1;i<=27;i++)cnt[i]+=cnt[i-1];
34     for(i=1;i<=n;i++)sa[cnt[v[i]]--]=i;
35     for(i=1;i<=n;i++)rnk[sa[i]]=rnk[sa[i-1]]+((s[sa[i]]==s[sa[i-1]])?0:1);
36     m=rnk[sa[n]];
37     for(j=1;j<n&&m<n;j*=2){//枚举长度 
38         for(t=0,i=n-j+1;i<=n;i++)x[++t]=i;
39         for(i=1;i<=n;i++){
40             if(sa[i]>j)x[++t]=sa[i]-j;
41         }
42         for(i=0;i<=m;i++)cnt[i]=0;
43         for(i=1;i<=n;i++)cnt[rnk[i]]++;
44         for(i=1;i<=m;i++)cnt[i]+=cnt[i-1];
45         for(i=n;i>=1;i--)sa[cnt[rnk[x[i]]]--]=x[i];
46         y[sa[1]]=1;
47         for(i=2;i<=n;i++)y[sa[i]]=y[sa[i-1]]+(cmp(sa[i],sa[i-1],j)?1:0);
48         for(i=1;i<=n;i++)rnk[i]=y[i];
49         m=rnk[sa[n]];
50     }
51     for(i=1;i<=n;i++){
52         if(rnk[i]==1){continue;}
53         k=h[rnk[i-1]]-1;//
54         b=sa[rnk[i]-1];
55         c=min(n-i,n-b)+1;
56         for(j=max(1,k);j<=c&&s[i+j-1]==s[j+b-1];j++);
57         h[rnk[i]]=j-1;
58     }
59     for(i=2;i<=n;i++){
60         if(ty(sa[i])!=ty(sa[i-1]))ans=max(ans,h[i]);
61     }
62     printf("%d\n",ans);
63 }
64 int main(){
65     while(scanf("%s %s",s+1,str+1)!=EOF)solve();
66     return 0;
67 }
后缀数组/基数排序
 1 #include<cstdio>
 2 #include<cstring>
 3 #include<iostream>
 4 #include<algorithm>
 5 using namespace std;
 6 const int M=2e5+5;
 7 int sa[M],cnt[M],rnk[M],n,m,len,v[M],h[M],x[M],y[M];
 8 char s[M],str[M];
 9 int S=95,d=0;
10 void pt(int a[]){
11     for(int i=1;i<=n;i++)printf("%d ",a[i]);puts("");
12 }
13 bool cmp(int a,int b){
14     if(rnk[a]!=rnk[b])return true;
15     int x,y;
16     x=(a+d>n)?0:rnk[a+d];
17     y=(b+d>n)?0:rnk[b+d];
18     return x!=y;
19 }
20 bool cmp_sa(int a,int b){
21     if(rnk[a]!=rnk[b])return rnk[a]<rnk[b];
22     int x=(a+d)>n?0:rnk[a+d];
23     int y=(b+d)>n?0:rnk[b+d];
24     return x<y;
25 }
26 int ty(int x){
27     if(x<len)return 1;
28     if(x>len)return 2;
29     return 0;
30 }
31 void solve(){
32     int i,j,k,a,b,c,ans=0;
33     len=strlen(s+1);
34     m=strlen(str+1);
35     s[++len]='`';
36     for(i=1;i<=m;i++)s[i+len]=str[i];
37     n=len+m;
38     for(i=1;i<=n;i++)sa[i]=i,rnk[i]=s[i];
39     sort(sa+1,sa+1+n,cmp_sa);
40     for(d=1;d<n&&m<n;d*=2){//枚举长度 
41         sort(sa+1,sa+1+n,cmp_sa);
42         
43         y[sa[1]]=1;
44         for(i=2;i<=n;i++){
45             y[sa[i]]=y[sa[i-1]]+(cmp(sa[i],sa[i-1])?1:0);
46         }
47         for(i=1;i<=n;i++)rnk[i]=y[i];
48         m=rnk[sa[n]];
49     }
50     for(i=1;i<=n;i++){
51         if(rnk[i]==1)continue;
52         k=h[rnk[i-1]]-1;//
53         b=sa[rnk[i]-1];
54         c=min(n-i,n-b)+1;
55         for(j=max(1,k);j<=c&&s[i+j-1]==s[j+b-1];j++);
56         h[rnk[i]]=j-1;
57     }
58     for(i=2;i<=n;i++){
59         if(ty(sa[i])!=ty(sa[i-1]))ans=max(ans,h[i]);
60     }
61     printf("%d\n",ans);
62 }
63 int main(){
64     while(scanf("%s %s",s+1,str+1)!=EOF)solve();
65     return 0;
66 }
后缀数组/快排

 

2016-12-30周五(50)

▲09:55:10 HDU4691 Front Compression 后缀数组 求一个字符串两个后缀的最长公共前缀.

  结论:LCP(sa[i],sa[j])=Min{h[k]}  k∈[i+1,j],那么原问题就转为了求静态区间最值RMQ问题->ST算法/线段树等.. 

  易错点:1)预处理Log2时,要注意优先级  ==比 &更高!!

     2)不能把log2作为变量名,首字母要大写

     3)倍增数组的j层在外面!!!!

▲14:07:22 POJ3294 Life Forms 后缀数组 二分+判定/单调队列尺取法 求出现次数超过K次不可重叠的最长子串.

▲16:15:11 POJ1743 Musical Theme 后缀数组+二分 求不可重叠最长重复子串  对于相对大小不变的情况,把差值作为权值->变成字符串匹配问题,注意首数字要和其他数字都不相同.题目要看清楚!!至少长度至少为5.

▲16:48:02 POJ3261 Milk Patterns 后缀数组+二分/单调队列  求出现次数至少为K次可重叠的最长子串

▲18:39:24 SPOJ694 Distinct Substring 求一个字符串所有不同的子串个数.后缀数组 把子串看作每个后缀的不同前缀,对于后缀i,只要去掉h[i]的部分就是答案了.

▲19:14:03 POJ2406 Power Strings 求字符串的循环节 KMP模板/后缀数组 求出每个后缀与原串的LCP,O(n)扫一遍即可. 但是后缀数组的倍增算法还是被卡T了 (T_T)


 

2016-12-31周六(51)

▲11:35:31 POJ3693 Maximum repetition substring 求出一个字符串中循环次数最多的子串,并且求出字典序最小的.后缀数组/RMQ ST算法.枚举循环节的长度,再枚举一定会经过的位置i,i+L,分别向后和向前匹配.在保证循环次数最多的情况下,再ST算法求出最小字典序.这里有个神奇的bug:求height数组时,要判断匹配的下标<=n!!!!!!调试毁人生!!!字典序最小真的太恶心了!!!


 

2017-01-01周日 Hello2017~(53)

▲10:24:55 BZOJ4539 树 (HHOI2016) 倍增+主席树:想到了正解,但是因为一个下标搞错了少了30分!!!【由于数据的神奇性????70%的数据n<=m!!】T_T

      思路是把每个子树看作一个小点,再构建一棵树,然后倍增.用主席树来求大树某个节点对应的原树的节点.复杂度O(nlogn).

  暴力做法:找到模板树,lower_bound来求出到大树上的对应编号,然后把子树dfs一遍,在大树上建边,询问求LCA.复杂度  m*n*logn+Qlog(m*n),能得30分.

▲15:34:45 BZOJ4538 网络(HNOI2016) 整体二分】/dfs序!!!  每次在比赛时想到整体二分就会卡住...每次求答案>mid的区间.只要考虑权值>mid的操作能否更新到每个询问,一个询问被某一个更新到:在要求出现的时间段内,并且不在路径上.因为二分,确定了它们的值>mid,所以我们不用再考虑操作的具体权值,那么就可以按照时间排序,那么问题转化成,对于询问i,判断之前是否存在不经过i上,并且没有消失的路径.对于每条路径,更新除了路径的所有点是不方便的,那么就可以考虑更新路径,判断又可以转化为是否现在存在的每条路径都经过i,那么现在就需要一个数据结构来完成 路径更新,单点查询->DFS序+BIT,对一条路径的消失,相当于更新值-1即可.当然对于路径更新,也可以用树剖或者LCT,但是常数较大.

  暴力做法:对于每个询问i,直接枚举所有存在的路径,判断否经过i点,直接暴力走向LCA.


 

2017-01-02周一(55)

▲11:12:08 BZOJ4537 最小公倍数(HNOI2016) 分块/并查集/启发式合并  问题转化为给出n点m边无向图,找一条路径(x,y)使得路径上边a,b的最大值分别为qa,qb.

      先考虑暴力:对于每个询问(x,y,qa,qb),找到所有a<=qa,b<=qb的边,加入,最后看x,y是否联通,并且联通块中最大值是否是a,b.(若要满足条件,图中不可能有大于a或者b的边,那么只考虑<=qa,<=qb的边.首先要保证两点可以联通,也要保证路径最大值为a,b.由于可以是非简单路径,所以只要联通块能保证,那么一定存在路径满足要求.)复杂度:Q*(m+n),20分可得.  

      接下来考虑优化:现在有a,b两维,可以通过排序减少一维.对一个询问(qa,qb),只考虑出现在询问之前的所有边,但由于b的限制,我们还需要把所有边根据b再排序,然后再插入,这样和暴力似乎没啥区别..这种做法低效的原因之一就是对于qa,qb相近的询问,进行了很多次重复的操作,那就可以想是不是能够把相近的询问放在一起,但又有两个条件限制,可以想到莫队算法.先把左端点根据所属块排序,每个块内再根据右端点排序,我们把a,b看成左右端点.具体解决方案:处理第i个块的询问时,把前i-1个块的所有边取出,和i块所有询问一起根据b排序,对于一个询问k,在前i-1个块里合法的边都已经加上了,但在第I块里还有一些合法的边,我们也把它加入.由于加入的边可能对这个块里其他的询问是不合法的,所以要记录下加边之前的状态.在处理完k之后暴力撤回.

      复杂度分析: 假设块的大小为S,处理每个块都要将边排序要mlogm,并查集不能路径压缩,但可以用启发式合并优化,总共m/S*(mlogm+nlogn),都要把每次询问都要暴力撤回,暴力添加块的内容,复杂度Q*S,总体复杂度m/S*(mlogm+nlogn)+Q*S,为了使复杂度尽可能小(视m,n,Q同阶).S≈sqrt(mlogm).

▲18:46:00BZOJ4556 字符串 (TJOI2016) 后缀数组/主席树/二分答案/RMQ.....谁知道暴力为什么这么快T_T...第一次rank1有点小激动!暴力的做法,二分,判定时直接从rk[c]往前扩展,直到MIn{h[k+1]..h[rk[c]]}<len,再扩展同时记录sa[k]是否在二分对应的区间中,往后扩展同理..这个暴力的做法居然碾压标程T T.只怪数据太水..


 

2017-01-03 周二(57)

▲11:37:15 POJ3415 Common Substrings 后缀数组/单调栈 题目并不难,注意单调性

      bug1:答案LL

      bug2:这是大bug!!!凡是要用到ST表预处理log数组,要for到n+3,所以要注意数组范围!!!  我一开始开了2e5+5,遇到两个长度为1e5的串,每个串后面加一个字符,然后再加3就刚好RE了 T_T..数组以后请开大点!!!

▲22:34:55 BZOJ4540 序列(HNOI2016)  分治/线段树/莫队/前缀和优化


 

2017-01-04周三 (58)

▲13:38:29BZOJ4542 大数(HNOI2016) 莫队算法/推性质

    数字[l,r]可以表示为 ∑ai*10^(r-i),把10^r提出   10^r*∑ai*10^(-i)%P=0 假设P不是2或5,那么10^r显然不能被p整除,所以 ∑ai*10^(-i)%P=0.把每一项乘上10^n,得到

∑ai*10^(n-i)  %P=0,i∈[L,R].假设si=∑ak*10^(n-k),k∈[1,i],那么可以通过s[r]-s[l-1]表示区间[l,r]的数值,那么只要s[r]=s[l-1],则数字模P余数为0.问题转化成求一个区间内,相同数字的对数->莫队算法.

▲20:16:23 UOJ70 新年的魔塔 提交答案/乱搞   注意提交答案题不要太拘泥于复杂度啊啊啊!!跑几个小时也没关系的!!


 

2017-01-05周四(61)

▲14:13:57 hihocoder 1445 后缀自动机2 求一个字符串不相同的子串个数/后缀自动机模板题

▲14:42:21 hihocoder 1449 后缀自动机3 求一个字符串长度为K的子串中出现最多的次数 K∈[1,len]     后缀自动机 利用了后缀自动机的树形性质

 1 #include<cstdio>
 2 #include<cstring>
 3 #include<algorithm>
 4 #include<iostream>
 5 #define ll long long
 6 using namespace std;
 7 const int M=2e6+10;
 8 const int S=26;
 9 int head[M],ec=1,to[M],nxt[M],cnt[M],ans[M],fa[M],son[M][S],mx[M],mn[M],m,tot=0,now,rt;
10 char s[M];//所有的子串个数->∑mx-mn+1 
11 void ins(int a,int b){
12     to[ec]=b;nxt[ec]=head[a];head[a]=ec++;
13 }
14 int New(int len){mx[++tot]=len;return tot;}
15 void Add(int a){
16     int x,y,v=now,z=New(mx[now]+1);
17     now=z;
18     cnt[z]=1;
19     while(v&&!son[v][a]){
20         son[v][a]=z;
21         v=fa[v];
22     }
23     if(!v){
24         mn[z]=1,fa[z]=1;
25         return;
26     }
27     x=son[v][a];
28     if(mx[x]==mx[v]+1){
29         mn[z]=mx[x]+1,fa[z]=x;
30         return;
31     }
32     y=New(mx[v]+1);
33     for(int i=0;i<S;i++)son[y][i]=son[x][i];
34     fa[y]=fa[x];
35     fa[x]=fa[z]=y;
36     mn[x]=mn[z]=mx[y]+1;
37     mn[y]=mx[fa[y]]+1;
38     while(v&&son[v][a]==x)son[v][a]=y,v=fa[v];
39 }
40 void dfs(int x){
41     for(int i=head[x];i;i=nxt[i]){
42         dfs(to[i]);
43         cnt[x]+=cnt[to[i]];
44     }
45     ans[mx[x]]=max(ans[mx[x]],cnt[x]);
46 }
47 void solve(){
48     int i,j,k,pre=0;
49     scanf("%s",s+1);
50     m=strlen(s+1);
51     rt=now=New(0);
52     for(i=1;i<=m;i++)Add(s[i]-'a');
53     for(i=1;i<=tot;i++)ins(fa[i],i);
54     dfs(1);
55     for(i=m;i>=1;i--)ans[i]=max(ans[i],ans[i+1]);
56     for(i=1;i<=m;i++)printf("%d\n",ans[i]);
57 }
58 int main(){
59     solve();
60     return 0;
61 } 
后缀自动机模板

▲20:53:13 hihocoder 1457 后缀自动机3  给出n个数字串,求出n个串中所有不同的数字串的和(mod 1e9+7 )

      对于一个状态x,找到所有的状态y,存在son[y][c]=x,也就是y添加一个字母c能得到x,那么sum[x]=sum[y]*10+c*size(y)

      对于一个串的情况,直接用拓扑排序搞,对于多个串,可以用后缀数组的技巧,通过某个符号把每个串连起来,这里就有个问题,计算答案时不能考虑含":"的子串.所以在拓扑排序过程中记录下来就可以了.


 

2017-01-06周五(62)

▲11:27:53 POJ2774 LongLong message 最长公共子串儿 后缀自动机 /根据一个串建立自动机,另一个串在上面运行,实时维护当前的最长公共子串即可.对于求多个串的LCS,还是根据一个串建立自动机,剩下的串分别在上面运行,对每个状态维护能匹配到的长度的min值,最后对每个状态取max就是答案.


 

2017-01-07周六(64)

▲09:44:42 HDU5008 Boring String Problem 给定一个串,Q个询问,每个询问求字典序第Ki小的子串,多个串则选择下标最小者.强制在线.

      后缀数组/每个串i,可以求出以sa[i]为起点的所有不同的串,并且是按照字典序排列的.那么只要二分就可以求出第K大.至于下标问题,先求出第一个第K小的串,然后向后找,找到所有包含这个串的后缀.可以暴力,也可以二分+RMQ.(暴力比二分+RMQ快真的不是我本意T_T)

▲14:26:18 UOJ 204 Boat 离散!!!

▲19:04:45 UOJ206 Gap 鸽巢原理

▲22:32:04 HDU4622 Reincarnation 后缀自动机+暴力 /hash+递推  hash做法:对于一个区间[l,r]若发现已经有区间[pos,pos+len-1]与s[l,l+len-1]相同,那么标记ans[l][l+len-1]++,ans[pos][l+len-1]--,保证对所有包含[pos,l+len-1]的区间答案只加一次.最后再递推出整个ans数组,表示[l,r]中所有不同的子串个数.回答O(1).


 

2017-01-08周日(67)

▲11:29:42 HDU5853 Jong Hyok and String 给定n个串,m个询问,每个询问给出一个串,问n个串中有多少个不同的子串和m在n个串里的出现位置的最后位置完全相同. 后缀自动机的裸题,直接构造出n个串的自动机,然后把每个询问的串跑一遍!!注意这里询问的是m串,而不是它的子串!!对于初始有多个串的情况,在每次一个串更新结束后,把now设为1即可!!(涨姿势!!)

▲14:51:58 hihocoder 1465 后缀自动机5 给定一个串s,和n个询问,求s中有多少个子串和a是循环同构的. 把a复制一下,然后在自动机上运行,预处理出每个状态在原串中出现的次数.

▲18:14:31 hihocoder 后缀自动机6 博弈/后缀自动机 给定两个串A,B.游戏这样规定,刚开始给出两个串的子串a,b,两个人可以在a或者b后面添加字符,使得添加后的a,b都是原串A,B的子串.若有人无法操作,则输.已知两人都足够聪明.现在求出能让先手获胜的所有方案的字典序第K小的方案.

    1.博弈-Sg函数.对于必败态->f(x)=0.必胜态 f(x)=mex{f(y)},表示f(y)不存在的最小的非负整数.当前局面为必败态,当且仅当所有节点的sg函数异或和为0.现在局面有两个节点,状态为x,y,仅当sg[x]!=sg[y]时是必胜态.那么只要根据定义求出每个状态的SG函数即可.根据son数组直接可以确定每个状态的所有合法后继状态.

    2.第K大:直接根据字典序dfs即可.

    也我真棒!!


 

2017-01-09 周一(71)

▲10:50:03 BZOJ3676 回文串 后缀自动机/Manacher/倍增/或后缀数组  用manacher得到所有的回文串,然后问题就转化为求每个串出现的次数.Manacher匹配过程中中出现的所有子串就是原串所有本质不同的回文子串.求一个子串出现次数用倍增优化常数!!快快快!!
【这里还有一坑——回文自动机】

▲13:31:25 BZOJ3238 差异 (AHOI2013) 后缀数组+单调栈/后缀自动机  后缀数组做法与POJ3145类似.后缀自动机做法很神!!!把串反过来,问题就转化成求任意两个前缀的最长公共后缀和.对于两个表示前缀的状态,它们最长公共后缀的长度就是在fa树上LCA的长度!!只要dfs一遍地推出来就好了!神!!

▲14:51:05 BZOJ3629 诸神眷顾的幻想乡(ZJOI2015) 后缀自动机 题目的关键在于这棵树不超过20个叶子节点!!那么任意两点的路径一定是两个叶子节点的子路径.所以直接枚举每个叶子,以它为根,建立后缀自动机.最后所有叶子建立后的自动机上的每个节点的mx-mn+1和就是答案.

▲22:36:13UOJ131 品酒大会(NOI2015) 后缀自动机/后缀数组  给定一个串,求所有后缀LCP>=r的方案数和权值乘积最大值.后缀自动机做法:把串反过来,问题就变成在树上,直接递推dp一下. 后缀数组做法:并查集.从大到小枚举r,再维护并查集,每次考虑h为r的点,更新再合并.同时维护最值.

 

posted @ 2016-12-30 14:10  LIN452  阅读(70)  评论(0编辑  收藏  举报