HDU4669_Mutiples on a circle

题目的意思是给你一些数字a[i](首位相连),现在要你选出一些连续的数字连续的每一位单独地作为一个数位。现在问你有多少种选择的方式使得选出的数字为k的一个倍数。

其实题目是很简单的。由于k不大(200),总共的数字个数为50000,所以我们对于当前走到的每一个数字最多的状态数目也只有50000*200个。

同时由于是循环的,我们可以使长度增倍,这样就可以保证序列是循环的了。

不过注意,增倍后空间是开不下的,所以需要使用循环的滚动数组。

同时注意递推的细节,还有有的a[i]不止一位数,不能直接取模。

#include <iostream>
#include <cstdio>
#include <cstring>
#define maxn 50050
typedef long long ll;
using namespace std;

int a[2*maxn],f[2*maxn],n,m,k,tep,cur,w[2*maxn];
int s[maxn][205];
ll ans;

void init_(int x)
{
    for (int i=0; i<k; i++) s[x][i]=0;
}

int count(int x)
{
    if (x<10) return 1;
    if (x<100) return 2;
    if (x<1000) return 3;
    return 4;
}

int get(int x)
{
    if (x==1) return 10;
    if (x==2) return 100%k;
    if (x==3) return 1000%k;
    return 10000%k;
}

int power(int x,int y)
{
    int tot=1;
    while (y)
    {
        if (y&1) tot=(tot*x)%k;
        y>>=1;
        x=(x*x)%k;
    }
    return tot;
}

int main()
{
    while (scanf("%d%d",&n,&k)!=EOF)
    {
        ans=0;
        w[0]=0,f[0]=0;
        for (int i=1; i<=n; i++)
        {
            scanf("%d",&a[i]);
            a[i+n]=a[i];
            w[i]=count(a[i]);
            w[i+n]=count(a[i+n]);
        }
        for (int i=1; i<=2*n; i++) f[i]=(f[i-1]*get(w[i])+a[i])%k;
        for (int i=1; i<=2*n; i++) w[i]+=w[i-1];
        for (int i=1; i<=n; i++)
        {
            init_(i);
            s[i][a[i]%k]++;
            for (int j=0; j<k; j++) s[i][(get(w[i]-w[i-1])*j+a[i])%k]+=s[i-1][j];
        }
        for (int i=n+1; i<=2*n; i++)
        {
            int qq=i-n-1;
            if (qq==0) qq=n;
            init_(i-n);
            s[i-n][a[i]%k]++;
            for (int j=0; j<k; j++) s[i-n][(get(w[i]-w[i-1])*j+a[i])%k]+=s[qq][j];
            cur=(f[i-n-1]*power(10,w[i]-w[i-n-1]))%k;
            cur=((f[i]-cur)%k+k)%k; 
            s[i-n][cur]--; 
            ans+=s[i-n][0];
        }
        printf("%I64d
",ans);
    }
    return 0;
}
如有转载,请注明出处(http://www.cnblogs.com/lochan)
原文地址:https://www.cnblogs.com/lochan/p/3446851.html