首页 > 代码库 > hihoCoder 1430 : A Boring Problem(一琐繁题)

hihoCoder 1430 : A Boring Problem(一琐繁题)

hihoCoder #1430 : A Boring Problem(一琐繁题)

时间限制:1000ms
单点时限:1000ms
内存限制:256MB

Description - 题目描述

  As a student of the school of electronics engineering and computer science in Peking University, Kyle took the course named Advanced Algebra in his freshman year, which, unluckily, became his nightmare.
  His teacher, Mr. X, has an approximately paranoid requirements in the ability of calculation, from which his students suffer a lot.
  One day, Mr. X got a whim that he wanted to know for a given integer k and a long numeric string S whose length is N, what is the result of  技术分享 for each i(1 ≤ i ≤ N), where 技术分享. S[L] means the Lth digit in S, and L starts from 1.
  Mr. X added the problem to the midterm test. Please give a hand to Kyle and tell him the answer mod 1000000007.

技术分享
作为北大信息科学技术学院的学生,Kyle曾经不知何为噩梦直到大一时候上了一门叫高等代数的课。
他的老师,Mr. X,对计算能力有着丧心病狂的要求。学生们饱受其害。
一天,Mr. X突发奇想,一个整数k与一个长度为N的数字串S,对于每个i(1 ≤ i ≤ N) 式子Σi j=1F(j, i)的结果是什么?这里F(j, i)=(Σi L=j S[L])^k。S[L]表示S的第L个数,L从1开始。
Mr. X把这道题放到了期中考试里。快帮帮Kyle 并告诉他模1000000007后的答案。
CN

 

Input - 输入

  There are multiple test cases.
  The first line of the input contains an integer T which means the number of test cases.
  The first line of each test case contains two integers, above mentioned N and k.
  The next line is the above mentioned string S. S consists of only digits(‘0 - ‘9‘).

技术分享
多组测试用例。
输入的第一行为一个整数T,表示测试用例的数量。
每组测试用例的第一行有两个整数,即上述的N与K。
下一行为一个数字串S。S仅包含数字(0 - 9)。
CN

 

Output - 输出

  For each test case, print a single line representing the result of  技术分享 for each i(1 ≤ i ≤ N).

技术分享
对于每个测试用例,输出一行数字表示每个i(1 ≤ i ≤ N)在式子Σi j=1F(j, i)中的结果。
CN

 

Sample Input - 样例输入

2
5 1
12345
5 1
54321

 

Sample Output - 样例输出

1 5 14 30 55
5 13 22 30 35

 

Note - 注意

  T ≤ 5
  N ≤ 50,000, k ≤ 100

 

题解

  为了防止和后面的说明混淆,字符串中的S[l]ai代替

  先按题目意思弄出几项,长得有点像01背包。

技术分享

 

  这个时候如果按上面的形式做的话,时间复杂度就是O(N*N*Log2K),会爆炸的。

  如果用技术分享看,中间的子串就不好表示……而且似乎没法化简(反正本渣没化简出来)。

然后加法不行,用减法?

 

 

  使用前N项和(前缀和)就能把每个子串用减法表示出来。

  此处Si表示前i项和,且S0 = 0

  式子转变如下:

技术分享

 

  再把每一项改写成二项展开式:

技术分享

  合并二项展开式

技术分享

  用SSi表示前iS之和,则可以得到通

技术分享

 

  这个时候时间复杂度就降到O(NK)了。

  注意取模与最后输出,然后就没什么问题了。

 

代码 C++

 1 #include <cstdio>
 2 #define mod 1000000007
 3 #define mx 50005
 4 #define ll long long
 5 ll c[105][105], s[mx][105], ss[mx][105];
 6 char rd[mx];
 7 int main(){
 8     int t, n, k, i, j;
 9     ll opt, tmp;
10     for (i = 0; i <= 100; ++i){
11         c[i][0] = 1;
12         for (j = 1; j <= i; ++j) c[i][j] = (c[i - 1][j - 1] + c[i - 1][j]) % mod;
13     }
14     for (i = 0; i < mx; ++i) s[i][0] = ss[i][0] = 1;
15 
16     for (scanf("%d", &t); t; --t){
17         scanf("%d%d ", &n, &k); gets(rd);
18         for (i = 0; i < n; ++i) s[i + 1][1] = rd[i] - 0;
19         for (i = 1; i <= n; ++i){
20             s[i][1] = (s[i][1] + s[i - 1][1]) % mod;
21             for (j = 2; j <= k; ++j) s[i][j] = (s[i][j - 1] * s[i][1]) % mod;
22         }
23         for (i = 1; i <= n; ++i){
24             for (j = 0; j <= k; ++j) ss[i][j] = (s[i][j] + ss[i - 1][j]) % mod;
25         }
26 
27         for (i = 1; i <= n; ++i){
28             opt = 0;
29             for (j = 0; j <= k; ++j){
30                 tmp = (c[k][j] * s[i][j]) % mod;
31                 if ((k - j) & 1) opt -= (tmp*ss[i - 1][k - j]) % mod;
32                 else  opt += (tmp*ss[i - 1][k - j]) % mod;
33                 opt %= mod;
34             }
35             printf("%lld%c", (opt + mod) % mod, " \n"[i == n]);
36         }
37     }
38     return 0;
39 }

 

hihoCoder 1430 : A Boring Problem(一琐繁题)