[6.27~7.4 做题记录]
[6.27~7.4 做题记录]
暑假集训,记录一些有意义 (多半无意义) 题目,不定时更新。
概率期望DP
OSU!
考虑设 \(E_i\) 为到第 \(i\) 次操作时的期望分数。我们发现从 \(x^3\) 到 \((x+1)^3\) 将加上 \(3x^2+3x+1\),我们考虑 \(x\) 和 \(x^2\) 的期望:
为什么呢,对于 \(x1\),有 \(p_i\) 的概率增加 \(1\)。\(x2\) 同理。
那么就有:
for(int i=1;i<=n;i++){ x1[i]=(x1[i-1]+1)*p[i]; x2[i]=(x2[i-1]+2*x1[i-1]+1)*p[i]; E[i]=E[i-1]+(3*x2[i-1]+3*x1[i-1]+1)*p[i]; } printf("%.1lf",E[n]);
灯灯灯
逆天推式子数奥题。
\(n\) 盏红灯,\(m\) 盏绿灯,考虑先假设剩下的是红灯,然后绿灯同理。
- 设剩下 \(k\) 盏红灯,那么剩下的第 \(k+1\) 盏一定为绿灯,那么只有前 \(n+m-k-1\) 盏灯是不确定的,其中有 \(n-k\) 盏红灯,\(m-1\) 盏绿灯,这 \(n+m-k-1\) 盏灯的顺序为 \(\tbinom{n+m-k-1}{n-k}\),而这所有的 \(n+m\) 盏灯有 \(\tbinom{n+m}{n}\) 种顺序,因此剩下 \(k\) 盏红灯的概率为 \(\dfrac{\binom{n+m-k-1}{n-k}}{\binom{n+m}{n}}\)。
- 那么,剩下红灯的期望为:
-
考虑化简上式,我们主要利用了两个等式:
- \(\tbinom{n}{m}=\tbinom{n-1}{m}+\tbinom{n-1}{m-1}\)
- \(\tbinom{n}{0}=\tbinom{n+1}{0}=\tbinom{n-1}{0}\)
-
分子化简:
\[= \tbinom{n+m-2}{n-1}\times 1+\tbinom{n+m-3}{n-2}\times 2+\tbinom{n+m-4}{n-3}\times 3 +\dots+\tbinom{n+m-n}{n-n+1}\times (n-1)+\tbinom{n+m-n-1}{n-n}\times n \]- 我们发现上式类似一个三角形,最后是 \(\tbinom{m}{1}\times (n-1)+\tbinom{m-1}{0}\times n\)。
- 由式2得 \(\tbinom{m}{1}\times (n-1)+\tbinom{m}{0}\times n\),再由式1得 \(\tbinom{m+1}{1}\times (n-1)+\tbinom{m}{0}\),接着再加上 \(\tbinom{m+1}{2}\times (n-2)\),可得 \(\tbinom{m+2}{2}\times (n-2)+\tbinom{m+1}{1}+\tbinom{m}{0}\)。
- 以此类推,逐层消消消,最后得到 \(\tbinom{m+n-1}{n-1}+\tbinom{m+n-2}{n-2}+\dots+\tbinom{m+2}{2}+\tbinom{m+1}{1}+\tbinom{m}{0}\)。
- 接着,我们由式2得到 \(\tbinom{m}{0}=\tbinom{m+1}{0}\),再用式1一个一个消,不再赘述。消完就是 \(\tbinom{m+n-1}{n}\)
-
最终得到结果为 \(\dfrac{\dbinom{m+n}{n-1}}{\dbinom{m+n}{n}}=\dfrac{n}{m+1}\)
-
最后剩下的绿灯同理,即为 \(\dfrac{m}{n+1}\)
-
答案即 \(\dfrac{n}{m+1}+\dfrac{m}{n+1}\)
double ans=1.0*n/(m+1)+1.0*m/(n+1); printf("%.6lf",ans);
[NOIP2016 提高组]换教室
首先认真读题,注意各个变量的含义
先用 \(\tt{Floyd}\) 跑出多源最短路,存储在 \(s_{i,j}\) 中。
设计DP,令 \(dp_{i,j,k}\) 表示递推到第 \(i\) 节课程,申请了 \(j\) 节课,以及本节课是否申请了 \((k\in [0,1])\) 的期望路程。哎我真是唐,开始觉得是申请成功j节,觉得m是申请成功的上界。申请是一回事,人家同意是另一回事...
接下来,就是 恶心的 转移方程:
为什么呢,由于这次不申请,那么此次必定在 \(c_i\) 上课,但是上一次可以申请也可以不申请,不申请则一定是从 \(c_{i-1}\) 出发,若申请则有 \(p_{i-1}\) 的概率从 \(d_{i-1}\) 出发,以及 \(1-p_{i-1}\) 的概率从 \(c_{i-1}\) 出发即可。
\(dp_{i,j,1}\) 同理,不再赘述,大力分讨。
CODE
#include<bits/stdc++.h> using namespace std; #define read read() #define pt puts("") inline int read{ int x=0,f=1;char c=getchar(); while(c<'0'||c>'9') {if(c=='-') f=-1;c=getchar();} while(c>='0'&&c<='9') x=(x<<3)+(x<<1)+c-'0',c=getchar(); return f*x; } #define T 2010 #define N 310 const int inf = 1e8; int t,m,n,e; int c[T],d[T]; double p[T]; int s[N][N]; void Floyed(){ for(int k=1;k<=n;k++) for(int i=1;i<=n;i++) for(int j=1;j<=n;j++) if(s[i][k]+s[k][j]<s[i][j]) s[i][j]=s[i][k]+s[k][j]; } double dp[T][T][2]; signed main() { t=read,m=read,n=read,e=read; for(int i=1;i<=t;i++) c[i]=read; for(int i=1;i<=t;i++) d[i]=read; for(int i=1;i<=t;i++) scanf("%lf",&p[i]); for(int i=1;i<=n;i++) for(int j=1;j<=n;j++) s[i][j]=(i==j?0:inf); for(int i=1,a,b,w;i<=e;i++){ a=read,b=read,w=read; s[a][b]=s[b][a]=min(s[a][b],w); } Floyed(); for(int i=1;i<=t;i++) for(int j=0;j<=t;j++) dp[i][j][0]=dp[i][j][1]=1e8; dp[1][0][0]=dp[1][1][1]=0; for(int i=2;i<=t;i++){ for(int j=0;j<=min(i,m);j++){ if(!j) dp[i][0][0]=dp[i-1][0][0]+s[c[i-1]][c[i]]; else{ dp[i][j][0]=min(dp[i-1][j][0]+s[c[i-1]][c[i]],dp[i-1][j][1]+(1-p[i-1])*s[c[i-1]][c[i]]+p[i-1]*s[d[i-1]][c[i]]); dp[i][j][1]=min(dp[i-1][j-1][0]+p[i]*s[c[i-1]][d[i]]+(1-p[i])*s[c[i-1]][c[i]],dp[i-1][j-1][1]+p[i-1]*p[i]*s[d[i-1]][d[i]]+p[i-1]*(1-p[i])*s[d[i-1]][c[i]]+(1-p[i-1])*p[i]*s[c[i-1]][d[i]]+(1-p[i-1])*(1-p[i])*s[c[i-1]][c[i]]); } } } double ans=1e9; for(int i=0;i<=m;i++) ans=min(ans,min(dp[t][i][0],dp[t][i][1])); printf("%.2lf",ans); return 0; }
[SCOI2008]奖励关
由于正推灰常不好推,我们考虑倒推。
发现 \(n\leq 15\),那么状压。
设 \(dp_{i,t}\) 表示从第 \(1\) 轮到第 \(i-1\) 轮取过的宝物状压为 \(t\),下面从第 \(i\) 轮到第 \(k\) 轮的最大期望分值。
那么当符合吃宝物 \(j\) 条件时,\(dp[i][t]=\max(dp_{i+1,t},dp_{i+1,t|2^j}+p_j)\)
最后 \(dp_{1,0}\) 即为所求。
我们发现,大多数概率期望DP都可以通过从唯一的末状态倒推回不确定的初状态。
CODE
#include<bits/stdc++.h> using namespace std; #define read read() #define pt puts("") inline int read{ int x=0,f=1;char c=getchar(); while(c<'0'||c>'9') {if(c=='-') f=-1;c=getchar();} while(c>='0'&&c<='9') x=(x<<3)+(x<<1)+c-'0',c=getchar(); return f*x; } int k,n; int p[16]; int s[16]; #define MAX ((1<<n)-1) double dp[110][1<<16]; signed main() { k=read,n=read; for(int x,i=1;i<=n;i++){ p[i]=read; x=read; while(x) s[i]|=1<<(x-1),x=read; } for(int i=k;i>=1;--i){ for(int t=0;t<=MAX;t++){ for(int j=1;j<=n;j++){ if((t|s[j])==t) dp[i][t]+=max(dp[i+1][t],dp[i+1][t|(1<<(j-1))]+p[j]); else dp[i][t]+=dp[i+1][t]; } dp[i][t]=dp[i][t]/n; } } printf("%.6lf",dp[1][0]); return 0; }
[NOI2005]聪聪与可可
一眼最短路,\(\tt{SPFA}\)(Wang54321称这是 \(\tt{BFS}\),称 \(\tt{SPFA}\) 碰瓷) 或者 \(\tt{Dijkstra}\) 堆优化,处理出多源最短路 \(s_{i,j}\),并处理出 \(nxt_{i,j}\) 表示猫在 \(i\),鼠在 \(j\) 时,猫靠近鼠的下一步应去往的位置。
进行 DP,还是一样,倒推 DP,设 \(dp_{i,j}\),表示猫在 \(i\),鼠在 \(j\),猫抓到鼠的期望时间。
考虑进行转移:
- 若此时猫鼠在一块,说明已经抓到。
- 若此时猫鼠距离小于等于2,则一步就可抓到。
- 否则,猫一定要向鼠走两步,即移动到 \(nxt_{nxt_{i,j},j}\),然后继续搜索,鼠会以 \(\frac{1}{k_j+1}\) 的概率走到相邻节点或是不走,直接求和即可,记得此时初始值为 \(1\),并记得加上留在原位的情况。
CODE
#include<bits/stdc++.h> using namespace std; #define read read() #define pt puts("") inline int read{ int x=0,f=1;char c=getchar(); while(c<'0'||c>'9') {if(c=='-') f=-1;c=getchar();} while(c>='0'&&c<='9') x=(x<<3)+(x<<1)+c-'0',c=getchar(); return f*x; } const int N = 1010; const int inf = 1e8; int n,m; int cat,rat; struct EDGE{ int next,to; }e[N<<1]; int head[N],tot; void add(int u,int v){ e[++tot]={head[u],v}; head[u]=tot; } double dp[N][N]; int k[N]; int dis[N][N],nxt[N][N]; queue<int>q; bool vis[N]; void SPFA(){ for(int i=0;i<=n;i++) for(int j=0;j<=n;j++) dis[i][j]=nxt[i][j]=inf; for(int i=1;i<=n;i++){ q.push(i); dis[i][i]=0;vis[i]=1; while(!q.empty()){ int x=q.front();q.pop(); vis[x]=0;int y; for(int j=head[x];j;j=e[j].next){ y=e[j].to; if(dis[i][x]+1<dis[i][y]){ dis[i][y]=dis[i][x]+1; if(!vis[y]) vis[y]=1,q.push(y); } } } } } void init(){ for(int x=1,y;x<=n;x++){ for(int i=head[x];i;i=e[i].next){ int j=e[i].to; for(int y=1;y<=n;y++){ if(dis[j][y]+1==dis[x][y]){ nxt[x][y]=min(nxt[x][y],j); } } } } } double DP(int u,int v){ if(dp[u][v]) return dp[u][v]; if(!dis[u][v]) return 0; if(dis[u][v]<=2) return 1; dp[u][v]=1; int t=nxt[nxt[u][v]][v]; for(int i=head[v];i;i=e[i].next){ int p=e[i].to; dp[u][v]+=DP(t,p)/(k[v]+1); } dp[u][v]+=DP(t,v)/(k[v]+1); return dp[u][v]; } signed main() { n=read,m=read; cat=read,rat=read; for(int a,b,i=1;i<=m;i++){ a=read,b=read;add(a,b),add(b,a); k[a]++;k[b]++; } SPFA(); init(); double ans=DP(cat,rat); printf("%.3lf",ans); return 0; }
[SHOI2014]概率充电器
大难题,贺了
首先,对于两件事情 \(A,B\),发生概率分别为 \(P(A),P(B)\),那么至少发生 \(A,B\) 其中一种的概率为:
由于每个概率对期望的贡献都乘1,因此答案就是每个节点来电概率之和。
那么我们考虑分讨,某节点的来电情况:
- 自己来电
- 儿子给电
- 父亲给电
规定 \(dp_i\) 表示节点 \(i\) 来电的概率,\(p_i\) 表示节点 \(i\) 自己来电的的概率,\(p(i,j)\) 表示 \(i,j\) 之间导线导电的概率。
考虑树形 DP,进行两次 \(\tt{DFS}\),第一次将儿子的信息传到父亲,即 \(up\);第二次将父亲信息传到儿子,即 \(down\)。
-
\(up\)
- 处理第一种情况,令 \(dp_i\) 的初始值为 \(p_i\) 即可。
- 处理第二种情况,考虑从儿子回溯时,加上儿子的贡献,即 \(dp_x=dp_x+dp_y\times p_{x,y}\),当然,此处的 \(+\) 应该是上文提到的 \(P(AB)=P(A)+P(B)-P(A)*P(B)\)。
-
\(down\)
- 处理第三种情况,首先求出父节点来电的概率 \(p_a\),我们发现由 \(up\) 可以得 \(p_a+p_b-p_a\times p_b=dp_x\),此时 \(p_b\) 仍为 \(dp_y\times p_{x,y}\),但是由于我们从上往下跑,此时 \(dp_x\) 已经包含了第三种情况,因此我们可以直接处理 \(p_a=\dfrac{dp_x-p_b}{1-p_b}\),然后 \(dp_y\) 就可以加上 \(p_a\times p_{x,y}\) 了,当然也是上文的方式。
-
最后 \(\sum \limits _{i=1}^{n} dp_i\)
CODE
#include<bits/stdc++.h> using namespace std; #define read read() #define pt puts("") inline int read{ int x=0,f=1;char c=getchar(); while(c<'0'||c>'9') {if(c=='-') f=-1;c=getchar();} while(c>='0'&&c<='9') x=(x<<3)+(x<<1)+c-'0',c=getchar(); return f*x; } const int N = 5e5+10; int n; struct EDGE{int next,to;double p;}e[N<<1];int head[N],tot; void add(int u,int v,double p){e[++tot]={head[u],v,p};head[u]=tot;} int p[N]; double dp[N]; double Plus(double pa,double pb){ return pa+pb-pa*pb; } void dfs_up(int x,int fa){ for(int i=head[x];i;i=e[i].next){ int y=e[i].to; if(y==fa) continue; dfs_up(y,x); dp[x]=Plus(dp[x],dp[y]*e[i].p); } } void dfs_down(int x,int fa){ for(int i=head[x];i;i=e[i].next){ int y=e[i].to; if(y==fa) continue; double pb=dp[y]*e[i].p; if(pb==1.0){ dfs_down(y,x); continue; } double pa=(dp[x]-pb)/(1-pb); dp[y]=Plus(dp[y],pa*e[i].p); dfs_down(y,x); } } double ans; signed main() { n=read; for(int a,b,c,i=1;i<n;i++){ a=read,b=read,c=read; add(a,b,c*0.01),add(b,a,c*0.01); } for(int i=1;i<=n;i++) p[i]=read,dp[i]=p[i]*1.0/100.0; dfs_up(1,0); dfs_down(1,0); for(int i=1;i<=n;i++){ ans+=dp[i]; } printf("%.6lf",ans); return 0; }
数位DP
数位DP,大体打法有两种,即递推和记搜,我比较Cai,只会记搜
花神的数论题
要求 \(\prod \limits _{i=1}^{N} sum(i)\),考虑枚举 \(sum(i)\),即枚举数中1的个数 \(k\),求出满足 \(sum(i)=k\) 的 \(i\) 的个数,然后快速幂求解即可。
这是板,考虑设计 \(dp_{i,j,k}\) 表示处理到第 \(i\) 位,当前已经有了 \(j\) 个 \(1\),我们的目标是 \(k\) 个 \(1\) 时的数的个数,然后进行记搜,为了确保比 \(n\) 小,我们再规定一个 \(limit\) 即可。
CODE
#include<bits/stdc++.h> using namespace std; #define mod 10000007 #define int long long #define read read() #define pt puts("") inline int read{ int x=0,f=1;char c=getchar(); while(c<'0'||c>'9') {if(c=='-') f=-1;c=getchar();} while(c>='0'&&c<='9') x=(x<<3)+(x<<1)+c-'0',c=getchar(); return f*x; } void write(int x){ if(x<0) putchar('-'),x=-x; if(x>9) write(x/10); putchar(x%10+'0'); return; } #define N 55 int n; int len,a[55]; int ans=1ll; int qpow(int x,int y){ int res=1; while(y){ if(y&1) res=res*x%mod; x=x*x%mod; y>>=1; } return res; } int dp[N][N][N][2],sum[N]; int DP(int p,int t,int s,bool limit){ if(t > s) return 0; if(p>len) return t==s; if(dp[p][t][s][limit]!=-1) return dp[p][t][s][limit]; //if(!limit && dp[p][t][s]!=-1) return dp[p][t][s]; 这是等价的,但是要注意下面给它赋值时要在 !limit 时才能赋值。 int up=limit?a[p]:1; int res=0; for(int i=0;i<=up;i++){ res+=DP(p+1,t+(i==1),s,i==up&&limit); } dp[p][t][s][limit]=res; return res; } signed main(){ n=read; for(len=0;n;n>>=1) a[++len]=n&1; reverse(a+1,a+len+1); memset(dp,-1,sizeof(dp)); for(int i=1;i<=50;i++){ sum[i]+=DP(1,0,i,1); } for(int i=1;i<=50;i++){ ans=ans*qpow(i,sum[i])%mod; } write(ans); return 0; }
类似的题还有 [0和1的熟练],题意是区间 \([l,r]\) 中有多少数字的二进制表示(不含前导零)中0的个数不少于1的个数的数的个数。一样的思路,可以枚举1的个数,然后注意判前导0的个数就好了。
CODE
#include<bits/stdc++.h> using namespace std; #define read read() #define pt puts("") inline int read{ int x=0,f=1;char c=getchar(); while(c<'0'||c>'9') {if(c=='-') f=-1;c=getchar();} while(c>='0'&&c<='9') x=(x<<3)+(x<<1)+c-'0',c=getchar(); return f*x; } void write(int x){ if(x<0) putchar('-'),x=-x; if(x>9) write(x/10); putchar(x%10+'0'); return; } #define N 35 int l,r; int a[N],n; int dp[N][N][N]; int DP(int p,int t,bool limit,int lead){ if(!p) return (n-t-lead>=t)?1:0; if(!limit && dp[p][t][lead]!=-1) return dp[p][t][lead]; int up=limit?a[p]:1;int res=0; for(int i=0;i<=up;i++) res+=DP(p-1,t+(i==1),limit&&(i==up),(lead==n-p)?lead+(i==0):lead); if(!limit) dp[p][t][lead]=res; return res; } int solve(int x){ int res=0; memset(dp,-1,sizeof(dp)); for(n=0;x;x>>=1) a[++n]=x&1; res+=DP(n,0,1,0); return res; } signed main(){ l=read,r=read; write(solve(r)-solve(l-1)); return 0; }
haha数
我们发现,若设原数为 \(x\),每一位为 \(a_i\),使 \(\forall a_i | x\),等价于使 \(\mathrm{lcm}_{i=1}^n\ {a_i} | x\)
因此,我们要记录各位上数的最小公倍数。那么原数怎么记录,我们知道 \(1\) ~ \(9\) 的最小公倍数为 \(2520\),那么一定有 \(\mathrm{lcm}_{i=1}^n\ {a_i} | 2520\),所以我们只要记录原数关于 \(2520\) 的余数即 \(x\% 2520\) 的值。
所以我们设 \(dp_{i,x,lcm}\),考虑记录当前位,原数除于 \(2520\) 的余数,和各位的最小公倍数。要开 \(20\times 2520\times 2520\),会爆,又发现各位可能的最小公倍数只有 \(48\) 种,我们可以把最后一维压成 \(50\),建立一一映射关系,然后直接记搜即可。
对于记搜有一个注意点,一定要从第 \(n\) 位往第 \(1\) 位倒序跑,我一开始甚至把 \(a\) 数组翻转,然后从 \(1\) 往 \(n\) 跑,这两种看似等价,实则不一样——考虑倒着跑是还剩 \(i\) 位,下一次可以直接继承以记搜,但是正跑就会 \(\text{WA}\),必须每次清空,就会 \(\text{T}\)。所以我们必须倒着跑,这也是最正确的记搜形式。
CODE
#include<bits/stdc++.h> using namespace std; #define ll long long #define read read() #define pt puts("") inline ll read{ ll x=0,f=1;char c=getchar(); while(c<'0'||c>'9') {if(c=='-') f=-1;c=getchar();} while(c>='0'&&c<='9') x=(x<<3)+(x<<1)+c-'0',c=getchar(); return f*x; } void write(ll x){ if(x<0) putchar('-'),x=-x; if(x>9) write(x/10); putchar(x%10+'0'); return; } #define mod 2520 int Gcd(int a,int b){return b?Gcd(b,a%b):a;} int Lcm(int a,int b){return b?a/Gcd(a,b)*b:a;} int mapp[2525],total; ll l,r; int a[20],n; ll dp[20][2525][50]; ll DP(int p,int r,int lcm,int limit){ if(!p) return (r%lcm==0)?1:0; if(!limit&&dp[p][r][mapp[lcm]]) return dp[p][r][mapp[lcm]]; int up=limit?a[p]:9; ll res=0; for(int i=0;i<=up;i++){ res+=DP(p-1,(r*10+i)%mod,Lcm(lcm,i),limit&(i==up)); } if(!limit) dp[p][r][mapp[lcm]]=res; return res; } ll solve(ll x){ for(n=0;x;x/=10) a[++n]=x%10; return DP(n,0,1,1); } signed main() { #ifndef ONLINE_JUDGE freopen("lty.in","r",stdin); freopen("lty.out","w",stdout); #endif for(int i=1;i<=2520;i++) if(mod%i==0) mapp[i]=++total; for(int T=read;T;--T){ l=read,r=read; ll ans=solve(r)-solve(l-1); write(ans);pt; } return 0; }
[ZJOI2010] 数字计数
递推一波,考虑预处理出 \(dp_{i,j,k}\) 为具有 \(i\) 位,最高位(即第 \(i\) 位)为 \(j\),数 \(k\) 出现的次数。转移方程很好想,
然后思索填数:
- 考虑有前导 \(0\) 的数,这些数一定小于 \(x\),直接不用考虑上界,枚举前导 \(0\) 的个数:\(\sum \limits _{i=1}^{n-1} \sum \limits _{j=1}^9 dp_{i,j,k}\)
- 考虑无前导 \(0\):
- 最高位不卡上界,后面还是随便填:\(\sum \limits _{i=1}^{s_n-1} dp_{n,i,k}\)
- 最高位卡上界,后面也要注意上界:\(\sum \limits _{i=1}^{n-1} \sum \limits _{j=1}^{s_i-1} dp_{i,j,k}\)
- 我们发现这样还是有漏情况——当你处理第 \(i\) 位时,你只加上了后面的第 \(i-1\) 位的贡献,而它本身还有后面 \(i-1\) 位数的大小的贡献没有算,最后枚举每一位处理即可。
CODE
#include<bits/stdc++.h> using namespace std; #define int long long #define read read() #define pt puts("") inline int read{ int x=0,f=1;char c=getchar(); while(c<'0'||c>'9') {if(c=='-') f=-1;c=getchar();} while(c>='0'&&c<='9') x=(x<<3)+(x<<1)+c-'0',c=getchar(); return f*x; } void write(int x){ if(x<0) putchar('-'),x=-x; if(x>9) write(x/10); putchar(x%10+'0'); return; } int a,b; int dp[15][15][15]; int p10[15]; int ans1[11],ans2[11]; int s[15],n; void solve(int x,int ans[]){ int X=x; for(n=0;x;x/=10) s[++n]=x%10; for(int k=0;k<=9;k++){ for(int i=n-1;i>=1;i--) for(int j=1;j<=9;j++) ans[k]+=dp[i][j][k]; for(int j=1;j<s[n];j++) ans[k]+=dp[n][j][k]; int num=0; for(int i=n-1;i>=1;--i) for(int j=0;j<s[i];j++) ans[k]+=dp[i][j][k]; } for(int i=n;i>=1;--i) ans[s[i]]+=X%p10[i-1]+1; } signed main(){ a=read,b=read; p10[0]=1;for(int i=1;i<=12;i++) p10[i]=p10[i-1]*10; for(int i=0;i<=9;i++) dp[1][i][i]=1; for(int i=2;i<=12;i++){ for(int j=0;j<=9;j++){ for(int k=0;k<=9;k++){ for(int p=0;p<=9;p++) dp[i][j][k]+=dp[i-1][p][k]; if(j==k) dp[i][j][k]+=p10[i-1]; } } } solve(a-1,ans1); solve(b,ans2); for(int i=0;i<=9;i++) write(ans2[i]-ans1[i]);putchar(' '); return 0; }
计数DP
有空再补
唉,多半是不补了,AFO了
闲话
\([2024.7.4]\) 中考出分了,以 \(663\) 分喜提全市 \(\tt{Rank} 2\),仅落后于 \(\tt{Rank} 1\) \(0.5\) 分。可是人们只会记得第一……
\([2024.7.4]\) 一下午加一晚上,虽然下午有个小体活,但是只打了8个题的博客还是太低效了……
\([2024.7.5]\) 学校OJ上的"一言"
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 单线程的Redis速度为什么快?
· SQL Server 2025 AI相关能力初探
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 展开说说关于C#中ORM框架的用法!