zoukankan      html  css  js  c++  java
  • CF1110H Modest Substrings

    Modest Substrings

    You are given two integers (L) and (R) . Let's call an integer (x) modest, if (L le x le R) .

    Find a string of length (n) , consisting of digits, which has the largest possible number of substrings, which make a modest integer. Substring having leading zeros are not counted. If there are many answers, find lexicographically smallest one.

    If some number occurs multiple times as a substring, then in the counting of the number of modest substrings it is counted multiple times as well.

    (1leq Lleq Rleq 10^{800},nleq 2000)

    题解

    先只考虑(leq R)的限制。回忆描述一个数(leq R)需要满足的条件:

    • (|x|< |R|)

      只要这些长度(< |R|)的子串开头不是(0)就要统计进答案。

      这个比较好做,直接在每个位置统计以它开头的子串的答案就行了。

    • (|x|=|R|)(x)(R)的LCP的下一位满足(x[i]< R[i])

      注意到这里需要知道(x)(R)的LCP,所以考虑AC自动机。

      我们只在这些串和(R)的LCP后第一个小于(R)的位置统计答案。那么只要在当前匹配到的节点选择的下一条出边小于(R[i]),并且我们要构造的串的剩余长度不小于某个下限,就要统计进答案。

      每个AC自动机节点还要对fail树上的祖先特殊处理一下。

    • (x=R)

      这些串直接在向AC自动机终止节点转移的边上统计答案就行了。

    回到原题要求(Lleq xleq R)。我们直接用统计(leq R)的减去统计(leq L-1)的就行了。

    时间复杂度(O(n(|L|+|R|)Sigma))

    CO int N=2e3+10;
    char L[N],R[N];
    int tot;
    int ch[N][10],fa[N],val[N][10][N];
    
    void insert(char str[],int n){
    	int x=0;
    	for(int i=1;i<=n;++i){
    		int c=str[i]-'0';
    		if(!ch[x][c]) ch[x][c]=++tot;
    		x=ch[x][c];
    	}
    }
    void build(char str[],int n,int v){
    	int x=0;
    	for(int i=1;i<=n;++i){
    		int c=str[i]-'0';
    		for(int d=x==0;d<c;++d) val[x][d][n-i+1]+=v; // edit 1: x=0 -> d starts from 1
    		if(i==n) val[x][c][1]+=v;
    		x=ch[x][c];
    	}
    }
    
    
    int F[N][N];
    
    int main(){
    	scanf("%s",L+1);
    	int lenL=strlen(L+1);
    	if(L[lenL]>'0') --L[lenL];
    	else{
    		int p=lenL;
    		while(p>1 and L[p]=='0') --p;
    		--L[p],fill(L+p+1,L+lenL+1,'9');
    	}
    	if(L[1]=='0' and lenL>1)
    		copy(L+2,L+lenL+1,L+1),L[lenL--]=0;
    	if(L[1]>'0') insert(L,lenL); // edit 2: L=0 -> don't care about it
    	
    	scanf("%s",R+1);
    	int lenR=strlen(R+1);
    	insert(R,lenR);
    	
    	deque<int> que;
    	for(int c=0;c<=9;++c)if(ch[0][c]) que.push_back(ch[0][c]);
    	while(que.size()){
    		int x=que.front();que.pop_front();
    		for(int c=0;c<=9;++c){
    			if(!ch[x][c]) {ch[x][c]=ch[fa[x]][c]; continue;}
    			fa[ch[x][c]]=ch[fa[x]][c],que.push_back(ch[x][c]);
    		}
    	}
    	
    	if(L[1]>'0') build(L,lenL,-1);
    	build(R,lenR,1);
    	
    	int n=read<int>();
    	for(int x=1;x<=tot;++x)for(int c=0;c<=9;++c) // edit 3: x starts from 1
    		for(int i=1;i<=n;++i) val[x][c][i]+=val[fa[x]][c][i];
    	
    	for(int x=0;x<=tot;++x)for(int c=1;c<=9;++c) // edit 4: c starts from 1
    		for(int i=1;i<lenL;++i) val[x][c][i]+=-1;
    	for(int x=0;x<=tot;++x)for(int c=1;c<=9;++c)
    		for(int i=1;i<lenR;++i) val[x][c][i]+=1;
    	
    	for(int x=0;x<=tot;++x)for(int c=0;c<=9;++c)
    		for(int i=1;i<=n;++i) val[x][c][i]+=val[x][c][i-1];
    	
    //	for(int x=0;x<=tot;++x)for(int c=0;c<9;++c)
    //		for(int i=1;i<=n;++i) cerr<<x<<" "<<c<<" "<<i<<" v="<<val[x][c][i]<<endl;
    	
    	for(int i=n-1;i>=0;--i)for(int x=0;x<=tot;++x){
    		F[i][x]=-1e9; // edit 5: initialize F=-inf
    		for(int c=0;c<=9;++c) F[i][x]=max(F[i][x],F[i+1][ch[x][c]]+val[x][c][n-i]);
    	}
    	printf("%d
    ",F[0][0]);
    	for(int i=0,x=0;i<n;++i){
    		int c=-1;
    		for(int d=0;d<=9;++d)
    			if(F[i][x]==F[i+1][ch[x][d]]+val[x][d][n-i]) {c=d; break;}
    		printf("%d",c);
    		x=ch[x][c];
    	}
    	return 0;
    }
    
  • 相关阅读:
    小程序那些坑
    2018-5-31 项目总结
    Android AndroidManifest学习笔记
    android 快捷键
    android的liveview装载数据
    android xml产生和解析
    SerializableMaplist传递数据
    android hander 线程用法
    DataGridView实现分页
    DataGridView添加另外一个控件。
  • 原文地址:https://www.cnblogs.com/autoint/p/12730933.html
Copyright © 2011-2022 走看看