HDU 4669 Mutiples on a circle (2013多校聯合7 1004)

http://acm.hdu.edu.cn/showproblem.php?pid=4669


Mutiples on a circle

Time Limit: 3000/1000 MS (Java/Others)    Memory Limit: 65535/65535 K (Java/Others)
Total Submission(s): 767    Accepted Submission(s): 209


Problem Description
Tom has a necklace with n jewels. There is a number on each jewel. Now Tom wants to select a wonderful chain from the necklace. A chain will be regarded wonderful if the wonderful value of the chain is a multiple of a key number K. Tom gets the wonderful value using this way:He writes down the number on the chain in clockwise order and concatenates them together. In this way, he gets a decimal number which is defined as the wonderful value.
For example, consider a necklace with 5 jewels and corresponding numbers on the jewels are 9 6 4 2 8 (9 and 8 are in neighborhood). Assume we take K=7, then we can find that only five chains can be multiples of K. They are 42, 28, 896, 42896 and 89642.

Now Tom wants to know that how many ways he can follow to select a wonderful chain from his necklace.
 

Input
The input contains several test cases, terminated by EOF.
Each case begins with two integers n( 1 ≤ n ≤ 50000), K(1 ≤ K ≤ 200),the length of the necklace and the key number.
The second line consists of n integer numbers, the i-th number ai(1 ≤ ai ≤ 1000) indicating the number on the ith jewel. It’s given in clockwise order.
 

Output
For each test case, print a number indicating how many ways Tom can follow to select a wonderful chain.

思路:我們首先可以先將環變成一條鏈,比如原來是 9 6 4 2 8,變成一條鏈後是 9 6 4 2 8 9 6 4 2,然後設原環有n個珠子,設dp[i][j]表示以第i個珠子結尾,向前最多包含n個珠子,組成的數模k餘j的數量。(這裏的第i個是指的鏈中的第i個,我們從第n個開始),比如對於樣例,以8結尾的就包括5種情況 8,28,428,6428,96428,五種情況,以9結尾的就有9,89,289,4289,64289,五種情況,其他易推之,我們首先暴力將以8結尾的所有情況O(n)地求出來,(這個太簡單了就不說了),然後考慮怎麼快速地轉移到以9爲結尾的方案,現在我們知道了以8結尾的所有狀態,爲了方便敘述,這裏用V8代替以8結尾的方案,V9代替以9爲結尾的方案。

易知,對於V8,除了96428這種情況,其他(n-1)中情況均可以直接轉移個V9,即8->89,28->289,428->4289,6428->64289,那麼對於這n-1中情況我們可以按模k分成k種情況,以O(k)的複雜度轉移,即dp[i-1][j]->dp[i][(j*10+9)%k],最後還要減去96428這種情況,然後加上單獨的9組成的情況,則我們完成了轉移,最後得到V9。


如果您已經理解了前面的例子,那麼這道題您已經做出了90%了,那還有10%是什麼呢?

這裏要注意的是珠子的權值的範圍是1到1000,所以我們在做這一步轉移的時候(dp[i-1][j]->dp[i][(j*10+9)%k])不能乘10,而是要乘以10^len,這裏的len是指第i個數的十進制位數,這個可以通過與處理得到。

另外,由於我們轉移的時候需要的信息僅僅是前一位的信息,所以我們不用設置dp[50000][200]這麼大的空間,而是可以採用滾動數組的思想,交換使用空間,可以節省大量空間。

以上是整體思路,代碼實現還有一些小的細節,具體實現可以參考下面的代碼。



#include <string.h>
#include <stdio.h>
#include <algorithm>
#define maxn 50010
#define ll long long
using namespace std;
int getlen(int x)
{
    int sum=1;
    while(x/10)
    {
        sum++;
        x/=10;
    }
    return sum;
}
int pow[200010];
int a[maxn],len[maxn];
int cout[2][210];
void init(int n,int mod)
{
    int i;pow[0]=1;
    for(i=1;i<=n;i++)
    pow[i]=(pow[i-1]*10)%mod;
}
int main()
{
  //  freopen("dd.txt","r",stdin);
    int n,k;
    while(scanf("%d%d",&n,&k)!=EOF)
    {
        int i;
        memset(cout,0,sizeof(cout));
        init(n*4,k);
        for(i=0;i<n;i++)
        {
            scanf("%d",&a[i]);
            len[i]=getlen(a[i]);
        }
        a[n]=a[0];
        len[n]=len[0];
        ll ans=0;
        int le=0,sum=0;
        for(i=n;i>0;i--)
        {
            sum=(a[i]*pow[le]+sum)%k;
            cout[0][sum]++;
            le+=len[i];
        }
        ans+=cout[0][0];
        int t=1;
        for(i=1;i<n;i++)
        {
            memset(cout[t],0,sizeof(cout[t]));
            for(int j=0;j<k;j++)
            {
                cout[t][(j*pow[len[i]]+a[i])%k]+=cout[1-t][j];
            }
            sum=(sum*pow[len[i]]+a[i])%k;
            cout[t][sum]--;
            cout[t][a[i]%k]++;
            ans+=cout[t][0];
            t=1-t;
            sum=((sum-a[i]*pow[le])%k+k)%k;
        }
        printf("%I64d\n",ans);

    }
    return 0;
}


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章