生活随笔
收集整理的這篇文章主要介紹了
【CF55D】Beautiful Numbers-数位DP+优化
小編覺得挺不錯的,現在分享給大家,幫大家做個參考.
測試地址:Beautiful Numbers
題目大意: 求在區間[L,R][L,R][L,R]中,有多少能整除自身所有非零數位的數。
做法: 本題需要用到數位DP+優化。
首先這題一看就是數位DP,本題的關鍵是狀態的設計以及優化。
我們很快能寫出一個狀態定義:f(i,j,k,0/1)f(i,j,k,0/1)f(i,j,k,0/1)表示前iii位,所有非零位的LCM是jjj,對jjj的余數是kkk,不卡/卡上界的數的數目。但我們發現這個東西不能轉移,當jjj增加的時候,新的余數會有很多種情況,因此我們不能考慮這種狀態定義。
我們發現,涉及到的所有的jjj都是LCM(1,2,...,9)=2520LCM(1,2,...,9)=2520LCM(1,2,...,9)=2520的因數,所以我們只需要kkk的表示改成對252025202520的余數,那么kkk是jjj的整數倍時,就表示這些數滿足題目中的條件。
這樣我們就能轉移了。但分析一下發現,時間復雜度是191919(位數)×2520×2520×log?2520\times 2520\times 2520\times \log 2520×2520×2520×log2520(求LCM)×2×10\times 2\times 10×2×10(枚舉轉移的位)×10\times 10×10(數據組數),T到沒邊,因此我們還要進一步進行優化。
打表或手算發現,不同的jjj的數目,也就是252025202520的因數,只有484848個,因此用一個數組映射一下這些數,jjj用映射后的值表示即可,優化掉一個505050。進而我們發現我們可以預處理出這些數之間的LCM,所以又優化掉了一個log?2520\log 2520log2520。進行這兩個優化后,已經非常接近時限了,但還是會T掉。進一步觀察發現,卡上界的情況中只有一個數,這個數的j,kj,kj,k是確定的,不用跟隨上面的枚舉,因此我們根本不用存0/10/10/1一維,只要維護當前上界的j,kj,kj,k即可完成應完成的轉移。所以又優化掉了一個222,就可以通過此題了,最大點的時間為3s3s3s左右。
以下是本人代碼:
#include <bits/stdc++.h>
using namespace std
;
typedef long long ll
;
const int LCM
=2520;
int T
,n
,s
[20],lcmlist
[1050],id
[3010],L
[50][10],tot
;
ll f
[21][LCM
+10][50];int gcd(int a
,int b
)
{return (b
==0)?a
:gcd(b
,a
%b
);
}int lcm(int a
,int b
)
{return a
*b
/gcd(a
,b
);
}ll
solve()
{memset(f
[n
+1],0,sizeof(f
[n
+1]));int toplcm
=1,toprem
=0;for(int i
=n
;i
>=1;i
--){memset(f
[i
],0,sizeof(f
[i
]));for(int j
=0;j
<LCM
;j
++)for(int k
=1;k
<=tot
;k
++)for(int now
=0;now
<=9;now
++)f
[i
][(j
*10+now
)%LCM
][L
[k
][now
]]+=f
[i
+1][j
][k
];if (i
<n
){for(int now
=0;now
<s
[i
];now
++)f
[i
][(toprem
*10+now
)%LCM
][L
[toplcm
][now
]]++;}for(int j
=1;j
<=((i
==n
)?(s
[i
]-1):9);j
++)f
[i
][j
][id
[j
]]++;toplcm
=L
[toplcm
][s
[i
]];toprem
=(toprem
*10+s
[i
])%LCM
;}ll ans
=0;for(int i
=1;i
<=tot
;i
++)for(int j
=0;j
*lcmlist
[i
]<LCM
;j
++)ans
+=f
[1][j
*lcmlist
[i
]][i
];if (toprem
%lcmlist
[toplcm
]==0) ans
++;return ans
;
}int main()
{scanf("%d",&T
);tot
=0;for(int i
=1;i
<(1<<9);i
++){int x
=1;for(int j
=1;j
<=9;j
++)if ((1<<(j
-1))&i
) x
=lcm(x
,j
);lcmlist
[++tot
]=x
;}sort(lcmlist
+1,lcmlist
+tot
+1);tot
=0;for(int i
=1;i
<(1<<9);i
++)if (i
==1||lcmlist
[i
]!=lcmlist
[tot
]){lcmlist
[++tot
]=lcmlist
[i
];id
[lcmlist
[tot
]]=tot
;}for(int i
=1;i
<=tot
;i
++)for(int j
=0;j
<=9;j
++)L
[i
][j
]=id
[lcm(lcmlist
[i
],max(j
,1))];while(T
--){ll x
;scanf("%I64d",&x
);x
--;ll ans
=0;if (x
){n
=0;while(x
){s
[++n
]=x
%10;x
/=10;}ans
-=solve();}scanf("%I64d",&x
);n
=0;while(x
){s
[++n
]=x
%10;x
/=10;}ans
+=solve();printf("%I64d\n",ans
);}return 0;
}
總結
以上是生活随笔為你收集整理的【CF55D】Beautiful Numbers-数位DP+优化的全部內容,希望文章能夠幫你解決所遇到的問題。
如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。