$SCOI2009\ windy$数 数位$dp$

\(Sol\)

数位\(dp\)常规套路题.

\(dp[i][j]\)表示从低位到高位填到第\(i\)位且第\(i\)位的数字为\(j\)的方案数.答案就是\(sol(r)-sol(l+1).\)这里\(dp\)的过程十分简单,一般出错的也就是计算小于等于\(x\)\(windy\)数,所以这里简述一下这个流程:

首先是预处理出\(x\)的位数\(ct\),\(a[i]\)表示数\(x\)从低到高第\(i\)位的数字是多少,然后累计第一种安全态的答案\(as+=\sum _{i=1}^{ct-1}\sum _{j=1}^{9}f[i][j]\).

接着累计第二种安全态的答案,也就是第\(ct\)位(从低到高)的数小于\(a[ct]\)的,\(as+=\sum_{j=1}^{a[ct]-1}f[ct][j]\).

最后也就是最容易出错的地方便是统计危险态的答案.假设当前填到第\(i\)位且\(i\)\(ct\)的数字都和\(x\)一样,那么第\(i-1\)位上填的数就有两个限制,一是要小于\(a[i-1]\),还有就是要与\(a[i]\)的差的绝对值要大于等于\(2\).

会发现上面的统计有两个漏洞,一是如果数\(x\)\(windy\)数,那么会少算一个,这个问题很好解决,判断一下就好.令一个问题是假设当前填到\(i\),且\(abs(a[i]-a[i-1])<=2\),那么下一位就不能填\(a[i-1]\),那么也就不用继续统计危险态的答案了,直接\(break\)掉.

\(Code\)

Code ```cpp #include #define il inline #define Ri register int #define go(i,a,b) for(Ri i=a;i<=b;++i) #define yes(i,a,b) for(Ri i=a;i>=b;--i) #define e(i,u) for(Ri i=b[u];i;i=a[i].nt) #define mem(a,b) memset(a,b,sizeof(a)) #define ll long long #define db double #define inf 2147483647 using namespace std; il int read() { Ri x=0,y=1;char c=getchar(); while(c<'0'||c>'9'){if(c=='-')y=-1;c=getchar();} while(c>='0'&&c<='9'){x=(x<<1)+(x<<3)+c-'0';c=getchar();} return x*y; } int f[20][10],a[20]; il void init() { go(i,0,9)f[1][i]=1; go(i,2,10) go(j,0,9) go(k,0,9) if(abs(j-k)>=2)f[i][j]+=f[i-1][k]; } il int sol(Ri x) { if(!x)return 0; Ri qvq=x,ct=0,ret=0;bool fl=1; while(qvq){a[++ct]=qvq%10;qvq/=10;} go(i,1,ct-1) go(j,1,9)ret+=f[i][j]; go(i,1,a[ct]-1)ret+=f[ct][i]; yes(i,ct,2) { go(j,0,a[i-1]-1) { if(abs(j-a[i])>=2)ret+=f[i-1][j]; } if(abs(a[i]-a[i-1])<2){fl=0;break;} } return ret+fl; } int main() { init(); Ri l=read(),r=read(); printf("%d\n",(sol(r)-sol(l-1))); return 0; } ```
posted @ 2019-09-26 16:48  DTTTTTTT  阅读(99)  评论(0编辑  收藏  举报