题意给你一个a,b,问a除b小数点后n位中,子串组合的数能被p整除的有多少个
a,b位1e16,n为1e6,p为300
思路首先,要先处理出来后小数点后n位,这个就是模拟一下除法,很简单(当时没想到哈)这是一个有意思的“迪屁”这个梗是去年,北大吉如一比赛之后和队友说:这比赛太垃圾了,满场都是数据结构,连一个有意思的迪屁都没有。
接下来就该转移了,转移方程为dp[i][x] = g[i - 1][y] + int(a[i] % p == x)这个表示,到i这一位,子串能构成x的倍数有多少个,那个y满足,(y + a[i]) % p=x,y=(x - a[i] + p) % p;当你转移完了这一位的时候,当前i位对i+1位的影响就是x * 10,所以转移的时候不要忘了乘10,来作为下一位的转移的根据。
每一位都是由上一位转移过来,所以就可以滚动一下,滚第一维
PS:一个有点玄学的地方就是,如果你把n和p开成long long 就wrong了,我至今也不知道怎么回事,及其玄学。
#include <cstdio>
#include <cmath>
#include <algorithm>
#include <queue>
#include <cstring>
#include <map>
using namespace std;
typedef long long LL;
const int N = 1e6;
LL A[N + 5] , dp[2][300] , g[2][300];
LL a , b ; int p ,n ;
int main()
{
int T;
scanf("%d",&T);
while(T--){
scanf("%lld %lld %d %d",&a , &b , &n , &p);
for(int i = 1 ; i <= n ; i++){
A[i] = (a * 10 / b);
a = a * 10 % b;
}
memset(dp , 0 , sizeof(dp));
memset(g , 0 , sizeof(g));
LL ans = 0;
int t = 0;
for(int i = 1 ; i <= n ; i++){
t = 1 - t;
memset(g[t] , 0 , sizeof(g[t]));
for(int x = 0 ; x < p ; x++){
int y = (x - A[i] + p) % p;
dp[t][x] = g[1 - t][y] + (LL)((A[i]) % p == x);
g[t][(x * 10) % p] += dp[t][x];
}
ans = ans + dp[t][0];
}
printf("%lld\n",ans);
}
return 0;
}