codeforces [Gym-100814K]

来源:互联网 发布:网络舆情信息相关知识 编辑:程序博客网 时间:2024/05/16 13:55

题意给你一个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;}