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
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.
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.
思路:我們首先可以先將環變成一條鏈,比如原來是 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;
}