[ZOJ 3296 Connecting the Segments]后缀数组、RMQ、贪心

【题目大意】

给一个串,让你求他最少由多少个可重叠的回文串所组成。输出ans-1(要拼接几次)

【算法分析】

令读入的串为s,s’为s翻转以后的字符串,然后再令S=s+’$’+s’。

对S建立后缀数组,height和lcp

然后枚举对称中心(注意,对称中心可能在字符上,也可能在字符之间,即回文串长度为偶数)

然后利用lcp求出最长覆盖区间。

然后问题变为用最少的区间去覆盖1~n这一段。

直接贪心:先将区间按L排序,然后枚举答案,看答案为ans时最多能覆盖到多远。

如果覆盖完整个1~n,那就可以输出了。

【其它】

TLE,WA了无限遍。。。

一个原因是因为一开始倍增算法里面用的C++的sort,后面改成基数排序就不超时了。

然后另一个原因是因为没有考虑到回文串为偶数长度时,对称轴不在字符上。

另外很不幸。。。我垫底了

果然大家用的都是DC3。。。只有我不会。。。

Submit Time Language Run Time(ms) Run Memory(KB) User Name 2010-02-21 17:03:31 C++ 70 1456 ConanKudo 2010-02-22 10:38:50 C++ 250 11148 lccycc 2010-02-21 19:42:45 C++ 400 20692 ILJ 2010-02-21 17:12:23 C++ 430 12416 zgmf_x20a 2010-02-22 17:53:00 C++ 510 21368 my icpc is going on,有要事先闪了 2010-02-22 17:56:12 C++ 510 21368 my icpc is going on,有要事先闪了 2010-02-22 17:50:59 C++ 520 22152 my icpc is going on,有要事先闪了 2010-02-22 17:48:57 C++ 540 26536 my icpc is going on,有要事先闪了 2010-02-22 18:22:56 C++ 550 23324 my icpc is going on,有要事先闪了 2010-02-22 18:21:04 C++ 600 23324 my icpc is going on,有要事先闪了 2010-02-22 22:52:08 C++ 720 13856 edward

【CODE】

#include #include #include #include #include using namespace std;
const int N=55555*2;
int n,e;
char S[N],lg[N];
int rank[N],sa[N],height[N],ls[N];
int lcp[N][18],TOT;
struct qj{int l,r;}d[N];
struct satmp{int a,b,pos;}a[N];
struct gtp{int next;satmp y;}g[N];

void init(){
    int i,j;
    n=strlen(S+1);
    S[n+1]=’$’;
    j=n+1;
    for (i=n;i>=1;i–){
        j++;
        S[j]=S[i];
    }
    n=j;
    lg[1]=0;
    for (int i=2;i<=n;i++)
      if (i==1<                      else lg[i]=lg[i-1];
}

void Jsort(){
    int i,t,tot;
    e=0; tot=0; memset(ls,0,sizeof(int)*(n+1));
    for (i=1;i<=n;i++){
        e++;
        g[e].y=a[i];
        g[e].next=ls[a[i].b];
        ls[a[i].b]=e;
    }
    for (i=1;i<=n;i++)
      for (t=ls[i];t;t=g[t].next)
        a[++tot]=g[t].y;
       
    e=0; tot=0; memset(ls,0,sizeof(int)*(n+1));
    for (i=n;i>=1;i–){
        e++;
        g[e].y=a[i];
        g[e].next=ls[a[i].a];
        ls[a[i].a]=e;
    }   
    for (i=1;i<=n;i++)
      for (t=ls[i];t;t=g[t].next)
        a[++tot]=g[t].y;
}   

inline bool cmp(satmp X,satmp Y){
    return X.a}

void GetRank(){
    int R=0;
    for (int i=1;i<=n;i++){
      if (!R || a[i].a!=a[i-1].a || a[i].b!=a[i-1].b) R++;
      rank[a[i].pos]=R;
    }   
}   
void build(){
    for (int i=1;i<=n;i++){a[i].a=S[i];a[i].b=0;a[i].pos=i;}
    sort(a+1,a+n+1,cmp);
    GetRank();
    for (int K=0;1<        int k=1<        for (int i=1;i<=n;i++){
            a[i].a=rank[i]; a[i].b=0;
            if (i+k<=n) a[i].b=rank[i+k];
            a[i].pos=i;
        }
        Jsort();
        GetRank();
    }
    for (int i=1;i<=n;i++) sa[rank[i]]=i;
}   

void makeheight(){
    height[1]=0;
    int i,j,k,st;
    for (i=1;i<=n;i++){
        if (rank[i]==1) continue;
        st=max(height[rank[i-1]]-1,0); j=i+st; k=sa[rank[i]-1]+st;
        while (j<=n && k<=n && S[j]==S[k]){j++;k++;st++;}
        height[rank[i]]=st;
    }
}

void initlcp(){
    for (int i=1;i<=n;i++) lcp[i][0]=height[i];
    for (int k=1;1<      for (int i=1;i+(1<        lcp[i][k]=min(lcp[i][k-1],lcp[i+(1<}

inline int Lcp(int l,int r){
    if (l>r) swap(l,r);
    l++;
    int k=lg[r-l+1];
    return min(lcp[l][k],lcp[r-(1<}   

void Ml(){
    int i,j=n+1,LCP;
    TOT=0;
    for (i=1;i<=n/2;i++){
        j–;
        LCP=Lcp(rank[i],rank[j]);
        TOT++;
        d[TOT].l=i-LCP+1;
        d[TOT].r=i+LCP-1;
    }
    j=n+1;   
    for (int i=1;i        j–;
        LCP=Lcp(rank[i+1],rank[j]);
        TOT++;
        d[TOT].l=i-LCP+1;
        d[TOT].r=i+LCP;
    }   
    n/=2;
}   

inline bool TXcmp(qj X,qj Y){
    return X.l}   

void TanXin(){
    sort(d+1,d+1+TOT,TXcmp);
    int now=0,ans,i=0,expand;
    for (ans=1;;ans++){
        expand=0;
        while (i<=TOT && d[i+1].l<=now+1){
            i++;
            if (d[i].r>expand) expand=d[i].r;
        }
        now=expand;
        if (now>=n){
            printf("%dn",ans-1);
            return;
        }   
    }   
}   

int main(){
    while (scanf("%s",S+1)!=EOF){
        init();
        build();
        makeheight();
        initlcp();
        Ml();
        TanXin();
        memset(S,0,sizeof(S));
    }   
}   

[ZOJ 3298 Crack]KMP

【题目大意】

给一个串A,一个串B,然后在B串中,从A串的某个位置开始匹配,然后匹配到A串末尾又匹配回A串开头,然后问最长的匹配段是多少?

如果A串在B串中没有一个正常的从1开始的匹配的话,输出bad。

【算法分析】

当时没想到,经lcc教导,终于明白了。。。ORZ!

先KMP。

我是开了一个邻接表,记录连续的完全匹配。

然后给每一个连续的完全匹配向前和向后延伸。然后去更新ans即可。

【CODE】

#include #include struct gtp{int y,next;}g[1001111];
int m,n,e;
int next[1111];
int P[1111],T[1001111];
int tot;
int belong[1001111],ls[1001111];

void makenext(){
    next[1]=0;
    int i,j=0;
    for (i=2;i<=m;i++){
        while (j>0 && P[j+1]!=P[i]) j=next[j];
        if (P[j+1]==P[i]) j++;
        next[i]=j;
    }   
}   

inline void addedge(int &x,int &y){
    e++;
    g[e].y=y; g[e].next=ls[x]; ls[x]=e;
}   

void kmp(){
    for (int i=1;i<=n;i++){
        belong[i]=0;
        ls[i]=0;
    }   
    tot=0;
    e=0;
    int i,j=0;
    for (i=1;i<=n;i++){
        while (j>0 && P[j+1]!=T[i]) j=next[j];
        if (P[j+1]==T[i]) j++;
        if (j==m){
            if (!belong[i-m]){
                tot++;
                belong[i]=tot;
            }
            else belong[i]=belong[i-m];
            addedge(belong[i],i);
            j=next[j];
        }   
    }   
}

void solve(){
    if (tot==0){
        printf("badn");
        return;
    }   
    int i,j,k,t,now,ans=0,num,st;
    for (i=1;i<=tot;i++){
        j=g[ls[i]].y+1;
        k=1;
        while (j<=n && k<=m && P[k]==T[j]){j++; k++;}
        j–; k–;
        now=k; num=0;
        for (t=ls[i];t;t=g[t].next){num++;st=g[t].y-m;}
        now+=m*num;
        j=st; k=m;
        while (j>=1 && k>=1 && P[k]==T[j]){j–; k–;}
        now+=m-k;
        if (now>ans) ans=now;
    }   
    printf("%dn",ans);
}   

int main(){
    for (;;){
        if (scanf("%d%d",&m,&n)==EOF) break;
        for (int i=1;i<=m;i++) scanf("%d",&P[i]);
        for (int i=1;i<=n;i++) scanf("%d",&T[i]);
        makenext();
        kmp();
        solve();
    }   
}   

ZOJ月赛——2010.2.21

【比赛经历】

这次月赛比较纠结,我大概1点半才开始做

然后一开始见到I题好多人A啊,然后就做I题去了,WA个不停。。。

最后发现,理解错了题目了,ORZ。。。于是重敲以后一交,AC。

无无聊聊的看了一下C题,不会做~

然后出来一看,F题AC人数暴增!

直接切进去,OH YEAH,原来是个排序+贪心,好像差不多10行的样子写完,交上去AC了。

回头看B题,AC人数也挺多的,再次切进去,然后看不懂continuous subsequence的意思。

金山词霸显示:n. 后继,随后

囧,到群里问了下,被痛斥了一下。。。终于知道连续子序列的意思。

然后仔细一思考,我只要搞到它满足所有的min,然后判断有没有超过max的即可。

所以如果只是想搞到他满足所有的min的话,用首尾指针维护即可。

一交,1A。

然后又去看了比较多人A的E题。哇。。。GDKOI出过这题的类似加强版!

于是就开敲,发现数据规模非常小,不需要用GDOI那种N^2的方法,直接DFS就行。

一交,居然WA了。。。检查了半天,发现是题目要求输出所有方案,而不是一个方案。。。囧。

改了以后终于AC。

然后剩下的时间已经不够8分钟了。索性不做,围观其他神牛。。。

最后rank:28

很大程度是因为睡懒觉。。。否则时间应该前移很多,5题也是有可能的。

rank围观地址:http://acm.zju.edu.cn/onlinejudge/showContestRankList.do?contestId=305

额,感叹一下,漆子超太无敌了。。。他还是单挑的。。。

表示对各路神牛的YM!

[UASCO 6.1.2 Serious challenges]动态规划

【算法分析】

这题我做过类似的,基本思想是做垂线,然后向左右延伸。

算法步骤的思想如下:

枚举每一个点,然后尽量向上延伸,直到碰到墙壁或者坏了的点,然后将垂直的线段向左右尽量延伸,最后结果就在这M*N个极大子矩形当中。

这个算法很容易证明:如果是答案的子矩阵,那么它一定上下左右都被某个点(或边界)卡住,否则他还可以延伸就不是最大的了。然后这个算法把每一个格子当做是最大矩形的上顶点(即阻止它向上延伸的顶点),然后向下延伸任意长度的各种情况都枚举了,那么向左右延伸,就必然可以包含最优解。

最后利用DP的思想减少重复,复杂度达到O(RC),使用滚动数组空间复杂度达到O(C)

【其它】

这题搞死我了,一直WA at 10,然后用pascal写了一遍,又是WA at 10

于是搞来搞去,最后发现原来是一个地方没有初始化,一交,终于AC,ORZ!!!

USACO终于圆满了,在此纪念一下

Chapter 1 DONE 2009.08.29 Getting started Chapter 2 DONE 2007.08.09 Bigger Challenges Chapter 3 DONE 2008.07.22 Techniques more subtle Chapter 4 DONE 2008.08.05 Advanced algorithms and difficult drills Chapter 5 DONE 2010.02.19 Serious challenges Chapter 6 DONE 2010.02.20 Contest Practice

这时间差好恐怖。。。

【CODE】

/*
ID:jie22221
TASK:rectbarn
LANG:C++
*/
#include #include #define min(x,y) (x)<(y)?(x):(y)
#define max(x,y) (x)>(y)?(x):(y)
const int INF=0x7FFFFFFF/5;
struct zb{int x,y;}s[31111];
int m,n,p,ans=0;
int u[2][3111],l[2][3111],r[2][3111],ll[3111],rr[3111];
bool v[2][3111];

void sort(int L,int R){
    if (L>=R) return;
    zb mid=s[(L+R)/2],tmp;
    int i=L,j=R;
    while (i        while (s[i].x        while (s[j].x>mid.x || s[j].x==mid.x && s[j].y>mid.y) j–;
        if (i<=j){
            tmp=s[i]; s[i]=s[j]; s[j]=tmp;
            i++; j–;
        }
    }
    sort(L,j);
    sort(i,R);
}

void init(){
    memset(s,0,sizeof(s));
    scanf("%d%d%dn",&m,&n,&p);
    for (int i=0;i      scanf("%d%dn",&s[i].x,&s[i].y);
    sort(0,p-1);
    for (int i=0;i<=n+1;i++){
        v[0][i]=true;
        l[0][i]=INF;
        r[0][i]=INF;
    }
}

void work(){
    int si=0,i,j,R,pi,tmp;
    for (R=1;R<=m;R++){
        i=R&1; pi=i^1;
        memset(v[i],false,sizeof(v[i]));
        v[i][0]=true; v[i][n+1]=true;
        for (j=1;j<=n;j++)
          while (si

              si++;
              v[i][j]=true;
          }
//      deal ll
        for (j=1;j<=n;j++)
          if (!v[i][j])
            if (v[i][j-1]) ll[j]=0;
                      else ll[j]=ll[j-1]+1;
//      deal rr
        for (j=n;j>=1;j–)
          if (!v[i][j])
            if (v[i][j+1]) rr[j]=0;
                      else rr[j]=rr[j+1]+1;
//      dp
        for (j=1;j<=n;j++)
          if (!v[i][j]){
              if (v[pi][j]){
                  u[i][j]=0;
                  l[i][j]=ll[j];
                  r[i][j]=rr[j];
              }
              else{
                  u[i][j]=u[pi][j]+1;
                  l[i][j]=min(ll[j],l[pi][j]);
                  r[i][j]=min(rr[j],r[pi][j]);
              }
              tmp=(l[i][j]+r[i][j]+1)*(u[i][j]+1);
              if (tmp>ans)
                ans=tmp;
          }
    }
}

int main(){
    freopen("rectbarn.in","r",stdin);
    freopen("rectbarn.out","w",stdout);
    init();
    work();
    printf("%dn",ans);
}

[USACO 6.1.1 Postal Vans]高精度、找规律

【题目大意】找一个特殊图哈密顿回路的方案数。

【算法分析】

我先DFS了一下,感觉如果列是4的话,应该是一个4阶的递归式。

然后找到规律。。。OH,YEAH,AC

规律就是F[n]=2F[n-1]+2F[n-2]-2F[n-3]+F[n-4]

【其它】1A

【CODE】

/*
ID:jie22221
TASK:vans
LANG:C++
*/
#include #include const int M=100;
const int Mod=100000;
struct bigint{int d[M+1];};
int px[4]={-1,1,0,0},py[4]={0,0,1,-1};
int n,limit,total,ans;
bigint f[1001];
bool v[5][5];

void dfs(int x,int y){
    if (total==4*limit-1){
        if (x+y==3) ans++;
        return;
    }
    int k,tx,ty;
    v[x][y]=true;
    total++;
    for (k=0;k<4;k++){
        tx=x+px[k]; ty=y+py[k];
        if (tx<1 || tx>4 || ty<1 || ty>limit || v[tx][ty]) continue;
        dfs(tx,ty);
    }   
    v[x][y]=false;
    total–;
}   

bigint plus(bigint x,bigint y){
    for (int i=1;i<=M;i++) x.d[i]+=y.d[i];
    for (int i=M;i>=1;i–){
        x.d[i-1]+=x.d[i]/Mod;
        x.d[i]%=Mod;
    }   
    return x;
}

bigint dec(bigint x,bigint y){
    for (int i=1;i<=M;i++) x.d[i]-=y.d[i];
    for (int i=M;i>=1;i–)
      while (x.d[i]<0){
          x.d[i]+=Mod;
          x.d[i-1]–;
      }   
    return x;
}   

void init(){
    memset(f,0,sizeof(f));
    for (limit=1;limit<=4;limit++){
        ans=0;
        total=0;
        dfs(1,1);
        f[limit].d[M]=ans;
    }
}

void put(bigint x){
    int st;
    for (st=0;st<=M;st++)
      if (x.d[st]) break;
    printf("%d",x.d[st]);
    for (int i=st+1;i<=M;i++){
        int div=Mod/10,mod=Mod;
        while (div>0){
            printf("%d",x.d[i]%mod/div);
            mod/=10;
            div/=10;
        }   
    }   
    printf("n");
}   

void work(){
    scanf("%d",&n);
    if (n<=4){
        printf("%dn",f[n].d[M]);
        return;
    }
    bigint tmp;
    for (int i=5;i<=n;i++){
       memset(tmp.d,0,sizeof(tmp.d));
       tmp=plus(f[i-1],f[i-1]);
       f[i]=plus(f[i],tmp);
       tmp=plus(f[i-2],f[i-2]);
       f[i]=plus(f[i],tmp);
       tmp=plus(f[i-3],f[i-3]);
       f[i]=dec(f[i],tmp);
       f[i]=plus(f[i],f[i-4]);
    }
    put(f[n]);
}   

int main(){
    freopen("vans.in","r",stdin);
    freopen("vans.out","w",stdout);
    init();
    work();
}