从重要的题开始
题解连接
1110H - Modest Substrings
题意:
求长度为n的字符串,最多有多少个子串x,满足L<=x<=R, |L|<=|R|<= 800 , n <= 2000 , 都是10进制表示
这是一道数位dp好题!感谢fdf大佬的指教。fdf的题解
我们把所有子串的贡献放在它第一次脱离限制的地方统计。而脱离限制可以枚举l或者r的前缀再枚举下一维统计。这里我们需要固定该串的长度,因为脱离限制后任意填,只要长度合法即可
如果[l] < |r| - 1,还需要统计长度介于两者之间的串。
把所有脱离限制的串插入AC自动机(记录每个状态所有脱离限制的合法长度),则只要匹配到该节点,并且长度合法即可贡献答案。并且所有fail树的祖先作为该状态的后缀也可以贡献答案。
f(i,u)表示长度为i,匹配到AC自动机上u,的最优答案。转移的时候预处理g(x,i)表示节点x,还剩长度i可以贡献的答案数。
因为要求字典序最小,所以记忆化搜索,逐位确定最优解
代码分类讨论写得很长,但是注释还是比较清晰的
总结:
L == R的特判打错了,调了1个小时。这样的错误真的必须仔仔细细的把每个代码的细节看清楚,不能借助对拍!
为什么还是对拍出错就知道哪里错了,而自己看代码却查不出来。必须改正自己调题的恶习,必须静心把每个地方的错误看出来!代码真的要一句一句读!
刻骨铭心的教训啊!
#include<bits/stdc++.h>
using namespace std;
#define rep(i,l,r) for(register int i = l ; i <= r ; i++)
#define repd(i,r,l) for(register int i = r ; i >= l ; i--)
#define rvc(i,S) for(register int i = 0 ; i < (int)S.size() ; i++)
#define rvcd(i,S) for(register int i = ((int)S.size()) - 1 ; i >= 0 ; i--)
#define fore(i,x)for (register int i = head[x] ; i ; i = e[i].next)
#define forup(i,l,r) for (register int i = l ; i <= r ; i += lowbit(i))
#define fordown(i,id) for (register int i = id ; i ; i -= lowbit(i))
#define pb push_back
#define prev prev_
#define stack stack_
#define mp make_pair
#define fi first
#define se second
#define lowbit(x) (x&(-x))
typedef long long ll;
typedef long double ld;
typedef unsigned long long ull;
typedef pair<int,char> pr;
const ll inf = 2e18;
const int N = 2e4 + 10;
const int maxn = 2020;
const ll mod = 1e9 + 7;
char s1[maxn],s2[maxn];
int a[maxn],b[maxn];
int lenl,lenr,n;
int f[maxn][N],nxt[N][10],fail[N],g[N][maxn],trans[N][10],tot;
void init(){
int cur = 0;
rep(i,1,lenl) a[i] = s1[i] - '0';
rep(i,1,lenr) b[i] = s2[i] - '0';
if ( lenl < lenr ){
//上、下界长度不同分开考虑
cur = 0;
rep(i,1,lenl){
rep(j,a[i] + 1,9){
//枚举脱离限制的地方。自由节点固定长度。每个子串在刚好脱离限制的时候统计答案、因为只要长度够一定贡献
if ( !nxt[cur][j] ) nxt[cur][j] = ++tot;
int x = nxt[cur][j];
g[x][lenl - i]++;
}
if ( !nxt[cur][a[i]] ) nxt[cur][a[i]] = ++tot;
cur = nxt[cur][a[i]];
}
g[cur][0]++;//刚好顶满下界也合法
cur = 0;
rep(i,1,lenr){
rep(j,0,b[i] - 1)