s_r_f
2020-04-28 10:28:25
几天前和
感觉如果不写代码的话就要变成口胡选手了
本文仅针对对
本文不会过多提及数位
数位
数位
数位
在状态比较多的题中
数位
其中
在状态中计入
提示:
下文中复杂度分析中可能存在字母
洛谷 P4317 花神的数论题
比较基础的数位
考虑记
为什么这个题里面没有考虑有没有前导
然后就可以直接回答
复杂度
LL dp[64][64]; bool vis[64][64];
inline LL DP(int n,int m){
if (n < m || m < 0) return 0; if (n == 0) return 1;
if (vis[n][m]) return dp[n][m]; vis[n][m] = 1;
return dp[n][m] = DP(n-1,m) + DP(n-1,m-1);
}
SP1433 KPSUM - The Sum
这个题有两种思路
一种是枚举交错和然后求出每种交错和的数的个数
另一种是直接把这些数作为一个整体来
记
为了
注意到
复杂度
代码见 题解链接
「BalticOI 2013」非回文数 Palindrome-Free Numbers
这题相比上一题的交错和而言而代码难度却大幅降低。
考虑如果存在回文
然后同样的
复杂度
LOJ评测链接
为啥我代码是LOJ上最优解啊(雾,不过70ms一共67个点,有没有人挑战一下卡到每个点只跑1ms啊
一个需要高精度
LOJ#6274. 数字
考虑数位
然后注意一下在转移
代码见评测链接
CF55D Beautiful numbers
双倍经验
首先不难设出状态
但如果直接设状态状态总数为
由于我们只需要考虑集合里面数的
如果真正合并干净了那就是
复杂度
SP8177题解链接
LL pw[19];
int trans[1<<9],tid[1<<9]; bool used[1<<9];
int cnt,v[73+5],nxt[73+5][10];
inline void init(){
int i,j,k,id,l = 1<<9,s;
for (pw[0] = i = 1; i <= 18; ++i) pw[i] = pw[i-1] * 10;
for (i = 0; i < l; ++i){
trans[i] = i;
for (j = 9; j >= 1; --j) if (trans[i] >> (j-1) & 1)
for (k = j + j; k <= 9; k += j) if (trans[i] >> (k-1) & 1){
trans[i] &= ((1<<9)-1)^(1<<(j-1)); break;
}
used[trans[i]] = 1;
}
for (i = 0; i < l; ++i) if (used[i]) ++cnt,v[cnt] = i,tid[i] = cnt;//cnt==73
for (i = 0; i < l; ++i) if (id = tid[i]){
for (v[id] = 1,j = 1; j <= 9; ++j) if (i>>(j-1)&1) v[id] = lcm(v[id],j);
for (nxt[id][0] = id,j = 1; j <= 9; ++j) nxt[id][j] = tid[trans[i|(1<<j-1)]];
}
}
inline bool check(int id,int r){ return r % v[id] == 0; }
LL dp[74][2520][20]; bool vis[74][2520][20];
inline LL DP(int s,int r,int n){
if (!n) return check(s,r) ? 1 : 0;
if (vis[s][r][n]) return dp[s][r][n]; vis[s][r][n] = 1;
LL tot = 0;
for (int i = 0; i <= 9; ++i) tot += DP(nxt[s][i],(r*10+i)%2520,n-1);
return dp[s][r][n] = tot;
}
[AHOI2009]同类分布
首先枚举余数
设
然后就可以过了
但是如果直接
剪枝之后不开
因为
所以复杂度
当然因为加了剪枝以及实际上的
const int S = 162+1;
LL dp[S][S][19]; bool vis[S][S][19]; int M;
inline void Clear(int mod){
register int i,j,k;
M = mod;
for (i = 0; i <= mod; ++i) for (j = 0; j < mod; ++j) for (k = 0; k <= 18; ++k) vis[i][j][k] = dp[i][j][k] = 0;
}
inline LL DP(int s,int r,int n){
if (s < 0 || s > n * 9) return 0; if (!n) return (s == 0 && r == 0) ? 1 : 0;
if (vis[s][r][n]) return dp[s][r][n]; vis[s][r][n] = 1;
LL tot = 0; for (int i = 0; i <= 9; ++i) tot += DP(s-i,(r*10+i)%M,n-1);
return dp[s][r][n] = tot;
}
inline LL solve(LL n){
int mod,i,j,x,s,r; LL tot = 0; bool flag = 0;
for (mod = 1; mod <= 162; ++mod){
Clear(mod); flag = 0;
s = r = 0;
for (i = 18; i >= 0; --i) if ((x = n/pw[i]%10) || flag){
for (j = 0; j < x; ++j) tot += DP(mod-s-j,(r*10+j)%M,i);
s += x,r = (r*10+x) % M; flag = 1;
}
}
return tot;
}
[SDOI2013]淘金
首先写个爆搜
数位
求出来之后
把
复杂度
代码见我的题解
CF908G New Year and Original Order
这题也有两种思路
一种思路是对于
记
这种思路的复杂度是
另一种思路是
记
那么对于一个数字答案显然为
所以对这个
记
处理一组询问的复杂度仍然是
这种做法的复杂度是
两种思路的代码见我的题解
P3791 普通数学题
考虑到这个题是
不难发现
然后就
怎么计算呢
设两维分别有
可以发现
然后答案就是两个
不难发现虽然询问有
所以复杂度为
注意这个复杂度里的
所以这个题就可以拿来加强了
代码
const int P = 998244353,MAX = 33;
map<LL,int>FF;
inline int F(LL n){
if (n <= 0) return 0; if (FF.count(n)) return FF[n];
LL sum = 0,l = 1,r;
while (l <= n) r = n/(n/l),sum = (sum + (r-l+1) * (n/l)) % P,l = r+1;
return FF[n] = sum;
}
inline LL get(int l,int r){ return (1ll<<r+1) - (1ll<<l); }
inline int calc(LL x,LL v1,LL v2,int a,int b){
if (a < b) swap(a,b); static LL s;
s = (x^v1^v2)&get(a,MAX);
return (1ll<<b)%P * (F(s+(1ll<<a)-1)-F(s-1)+P) % P;
}
LL n,m,x,ans;
int main(){
int i,j; LL v1,v2;
cin >> n >> m >> x; ++n,++m;
for (i = MAX,v1 = 0; i >= 0; --i) if (n>>i&1){
for (j = MAX,v2 = 0; j >= 0; --j) if (m>>j&1) ans += calc(x,v1,v2,i,j),v2 += 1ll<<j;
v1 += 1ll<<i;
}
cout << (ans%P+P)%P << '\n';
return 0;
}
CF582D Number of Binominal Coefficients
数位
考虑
设
复杂度
这个题目为什么不能用
因为
int dp[N][N][2][2];
bool vis[N][N][2][2];
inline LL s(int l,int r){ return l > r ? 0 : (l+r) * (r-l+1ll) / 2 % P; }
inline LL calc(int n){
if (n < 0) return 0; if (n < p) return 1ll * (n+1) * (n+2) / 2 % P;
n = min(n,p*2-2);
return (1ll * (n-p+2) * p % P + s(-p+n+2,p-1)) % P;
}
inline LL calc(int l,int r){ l = max(l,0),r = min(r,p*2-2); return (l > r) ? (0) : (calc(r) - calc(l-1) + P) % P; }
inline LL DP(int n,int m,int s1,int s2){
m = max(m,0);
if (!n) return (!s2 && !m) ? 1 : 0;
if (vis[n][m][s1][s2]) return dp[n][m][s1][s2]; vis[n][m][s1][s2] = 1;
int ans = 0;
if (s1){
if (!s2){
upd(ans,calc(0,p-2) * DP(n-1,m-1,1,1) % P);
upd(ans,calc(0,p-1) * DP(n-1,m,1,0) % P);
}
else{
upd(ans,calc(p-1,p+p-2) * DP(n-1,m-1,1,1) % P);
upd(ans,calc(p,p+p-1) * DP(n-1,m,1,0) % P);
}
}
else{
if (!s2){
upd(ans,calc(0,A[n]-1) * DP(n-1,m,1,0) % P);
upd(ans,calc(A[n],A[n]) * DP(n-1,m,0,0) % P);
upd(ans,calc(0,A[n]-2) * DP(n-1,m-1,1,1) % P);
upd(ans,calc(A[n]-1,A[n]-1) * DP(n-1,m-1,0,1) % P);
}
else{
upd(ans,calc(p,p+A[n]-1) * DP(n-1,m,1,0) % P);
upd(ans,calc(p+A[n],p+A[n]) * DP(n-1,m,0,0) % P);
upd(ans,calc(p-1,p+A[n]-2) * DP(n-1,m-1,1,1) % P);
upd(ans,calc(p+A[n]-1,p+A[n]-1) * DP(n-1,m-1,0,1) % P);
}
}
dp[n][m][s1][s2] = ans;
return ans;
}
CF585F Digits of Number Pi
可以利用
然后数位
dp部分代码
pair<int,int> trans[V][D][10];
struct SuffixAutomation{
int cnt,ed; int ch[V][10],fa[V],len[V];
inline void init(){ cnt=ed=1; }
inline void extend(int c){
int p = ed,np = ++cnt; len[np] = len[p] + 1;
while (p && !ch[p][c]) ch[p][c] = np,p = fa[p];
if (!p) fa[np] = 1;
else{
int q = ch[p][c];
if (len[q] == len[p] + 1) fa[np] = q;
else{
int nq = ++cnt;
fa[nq] = fa[q],len[nq] = len[p]+1,memcpy(ch[nq],ch[q],40);
fa[q] = fa[np] = nq;
while (ch[p][c] == q) ch[p][c] = nq,p = fa[p];
}
}
ed = np;
}
inline pair<int,int> getnxt(int now,int l,int c){
while (fa[now] && !ch[now][c]){
now = fa[now]; if (l > len[now]) l = len[now];
}
if (ch[now][c]) now = ch[now][c],++l;
if (l >= (m>>1)) now = l = -1;
return make_pair(now,l);
}
inline void get(){
int i,j,k;
for (i = 1; i <= cnt; ++i) for (j = 0; j <= (m>>1); ++j) for (k = 0; k <= 9; ++k)
trans[i][j][k] = getnxt(i,j,k);
}
}SAM;
int f[2][2][M]; bool visf[2][2][M];
inline int F(int tx,int ty,int i){
if (i > m) return 1;
if (visf[tx][ty][i]) return f[tx][ty][i]; visf[tx][ty][i] = 1;
int r = 0,c;
for (c = 0; c <= 9; ++c){
if (!tx && c < x[i]) continue;
if (!ty && c > y[i]) continue;
upd(r,F(tx|(c>x[i]),ty|(c<y[i]),i+1));
}
return f[tx][ty][i] = r;
}
int g[2][2][M][V][D]; bool visg[2][2][M][V][D];
inline int G(int tx,int ty,int i,int now,int l){
if (i > m) return 0;
if (visg[tx][ty][i][now][l]) return g[tx][ty][i][now][l]; visg[tx][ty][i][now][l] = 1;
int r = 0,c; pair<int,int>t;
for (c = 0; c <= 9; ++c){
if (!tx && c < x[i]) continue;
if (!ty && c > y[i]) continue;
t = trans[now][l][c];
if (t.first != -1) upd(r,G(tx|(c>x[i]),ty|(c<y[i]),i+1,t.first,t.second));
else upd(r,F(tx|(c>x[i]),ty|(c<y[i]),i+1));
}
return g[tx][ty][i][now][l] = r;
}
4、更加复杂的例子
牛客挑战赛40C-小V和字符串
首先考虑
不难发现我们有一种按位置扫描的暴力
然后按这个数位
int visc[N][2][2][N<<1]; int dpcnt[N][2][2][N<<1];
inline int Cnt(int pos,int bs,int ab,int sta){
if (pos == n+1) return sta == 1002 ? 1 : 0;
if (visc[pos][bs][ab][sta]) return dpcnt[pos][bs][ab][sta]; visc[pos][bs][ab][sta] = 1;
int tot = 0;
for (int va = 0; va <= 1; ++va) for (int vb = 0; vb <= 1; ++vb){
if (va>vb&&!ab) continue;
if (vb>S[pos]-'0' && !bs) continue;
upd(tot,Cnt(pos+1,bs||(vb<(S[pos]-'0')),ab||(va<vb),sta + va - vb));
}
return dpcnt[pos][bs][ab][sta] = tot;
}
int viss[N][2][2][N<<1]; int dps[N][2][2][N<<1];
inline int Sum(int pos,int bs,int ab,int sta){
if (pos == n+1) return 0;
if (viss[pos][bs][ab][sta]) return dps[pos][bs][ab][sta]; viss[pos][bs][ab][sta] = 1;
int tot = 0,vv;
for (int va = 0; va <= 1; ++va) for (int vb = 0; vb <= 1; ++vb){
if (va>vb&&!ab) continue;
if (vb>S[pos]-'0' && !bs) continue;
upd(tot,Sum(pos+1,bs|(vb<S[pos]-'0'),ab|(va<vb),sta + va - vb));
vv = 0;
if (va == vb) vv = 0;
else if (sta == 1002){
if (va == vb) vv = 0; else vv = P-pos;
}
else if (sta < 1002){
if (va) vv = pos; else vv = P-pos;
}
else if (sta > 1002){
if (va) vv = P-pos; else vv = pos;
}
upd(tot,(LL)vv*Cnt(pos+1,bs|(vb<S[pos]-'0'),ab|(va<vb),sta + va - vb)%P);
}
return dps[pos][bs][ab][sta] = tot;
}
P1836 数页码
P4999 烦人的数学作业
[CQOI2016]手机号码
[SCOI2009] windy 数