zoukankan      html  css  js  c++  java
  • 「bzoj 4180: 字符串计数」

    题目

    真是一道好题

    首先根据一个非常显然的贪心,如果给出了一个串(S),我们如何算最小操作次数呢

    非常简单,我们直接把(S)拉到(T)(SAM)上去跑,如果跑不动了就停下来,重新回到(1)继续跑

    于是我们建出一个(SAM)之后可以写一个这样的暴力,设(d[i][j][k])表示从(i)点到(j)点走(i)条边的最长路,对于那些走不动的边,我们可以接到(1)号节点对应的出边上去,边权为(1),其余的边权为(0),矩阵优化一下就是(O(|T|^3logn))的复杂度

    显然(|T|)并不允许我们开下如此之大的转移矩阵,尝试换一个角度来考虑这个问题

    我们发现我们问题的本质就是最大化最小值,这是不是可以二分一下呢

    于是现在的问题变成了对于一个二分出的操作次数(mid),判断答案是否能够更大

    显然我们如果使用(mid)此操作构造出来的串长度小于(n),那么我们就可以断定答案可能会更大一些

    于是又把问题转化成了利用(mid)次操作构造出来的字符串的最小长度

    这个如何求呢,我们考虑一次操作无非就是从(1)的某一个出边指向的节点到另一个(1)的出边指向的节点,所以我们求出这些节点两两之间的最短路就好了

    于是我们现在又可以利用矩阵转移了,复杂度(O(|T|+|c|^3log^2n)),(|c|)为字符集大小

    代码

    #include<algorithm>
    #include<iostream>
    #include<cstring>
    #include<cstdio>
    #define max(a,b) ((a)>(b)?(a):(b))
    #define min(a,b) ((a)<(b)?(a):(b))
    #define LL long long
    #define re register
    #define inf 922337203685477580
    #define maxn 200005
    LL n;
    char S[maxn>>1];
    int m,lst=1,cnt=1,tot;
    int len[maxn],fa[maxn],son[maxn][4],q[maxn],vis[maxn],c[maxn];
    LL a[4][4],ans[4][4],t[4][4],d[maxn];
    inline void ins(int c) {
    	int p=++cnt,f=lst;lst=p;
    	len[p]=len[f]+1;
    	while(f&&!son[f][c]) son[f][c]=p,f=fa[f];
    	if(!f) {fa[p]=1;return;}
    	int x=son[f][c];
    	if(len[f]+1==len[x]) {fa[p]=x;return;}
    	int y=++cnt;len[y]=len[f]+1;
    	fa[y]=fa[x],fa[x]=fa[p]=y;
    	son[y][0]=son[x][0];son[y][1]=son[x][1];
    	son[y][2]=son[x][2];son[y][3]=son[x][3];
    	while(f&&son[f][c]==x) son[f][c]=y,f=fa[f];
    }
    inline void did_t() {
    	LL mid[4][4];
    	for(re int i=0;i<4;i++)
    		for(re int j=0;j<4;j++) mid[i][j]=t[i][j],t[i][j]=inf;
    	for(re int k=0;k<4;k++)
    		for(re int i=0;i<4;i++)
    			for(re int j=0;j<4;j++)
    				t[i][j]=min(t[i][j],mid[i][k]+mid[k][j]);
    }
    inline void did_ans() {
    	LL mid[4][4];
    	for(re int i=0;i<4;i++)	
    		for(re int j=0;j<4;j++) mid[i][j]=ans[i][j],ans[i][j]=inf;
    	for(re int k=0;k<4;k++)	
    		for(re int i=0;i<4;i++)
    			for(re int j=0;j<4;j++)
    				ans[i][j]=min(ans[i][j],mid[i][k]+t[k][j]);
    }
    inline LL solve(LL now) {
    	for(re int i=0;i<4;i++)
    		for(re int j=0;j<4;j++) t[i][j]=a[i][j],ans[i][i]=inf;
    	for(re int i=0;i<4;i++) ans[i][i]=0;
    	LL b=now;
    	while(now) {if(now&1ll) did_ans();now>>=1ll;did_t();}
    	LL tmp=inf;
    	for(re int i=0;i<4;i++)
    		for(re int j=0;j<4;j++) tmp=min(tmp,ans[i][j]);
    	return tmp+b;
    }
    inline int check(LL now) {return solve(now)<=n;} 
    int main() {
    	scanf("%lld",&n);scanf("%s",S+1);m=strlen(S+1);
    	for(re int i=1;i<=m;i++) ins(S[i]-'A');
    	for(re int i=0;i<4;i++)
    		for(re int j=0;j<4;j++) a[i][j]=inf;
    	for(re int i=0;i<4;i++) {
            tot=0;q[++tot]=son[1][i];
            memset(vis,0,sizeof(vis));
            memset(d,20,sizeof(d));d[q[1]]=0;
            for(re int j=1;j<=tot;j++) {
                int x=q[j];
                for(re int k=0;k<4;k++) {
                    if(vis[son[x][k]]) continue;
                    if(!son[x][k]) a[i][k]=min(a[i][k],d[x]);
                    else vis[son[x][k]]=1,d[son[x][k]]=d[x]+1,q[++tot]=son[x][k];
                }
            }
        }
    	LL l=1,r=n,ans=0;
    	while(l<=r) {
    		LL mid=l+r>>1ll;
    		if(check(mid)) l=mid+1,ans=mid;
    			else r=mid-1;
    	}
    	if(solve(ans)<n) ans++;
    	printf("%lld
    ",ans);
    	return 0;
    }
    
  • 相关阅读:
    CentOS虚拟机和物理机共享文件夹实现
    集训第六周 数学概念与方法 概率 数论 最大公约数 G题
    集训第六周 数学概念与方法 概率 F题
    集训第六周 E题
    集训第六周 古典概型 期望 D题 Discovering Gold 期望
    集训第六周 古典概型 期望 C题
    集训第六周 数学概念与方法 UVA 11181 条件概率
    集训第六周 数学概念与方法 UVA 11722 几何概型
    DAG模型(矩形嵌套)
    集训第五周 动态规划 K题 背包
  • 原文地址:https://www.cnblogs.com/asuldb/p/10526278.html
Copyright © 2011-2022 走看看