bzoj3114 LCM Pair Sum

题意:以质因数分解的方式给定n,求所有满足:lcm(a, b) = n的无序数对的价值和。其中(a, b)的价值为a + b

解:

定义首项为a,公比为q,项数为n的等比数列的和为getQ(a, q, n)

首先考虑只有一个质因数,例如4。

有如下数对:(1, 4), (2, 4), (4, 4)

可得答案为getQ(1, 2, 2) + 43

然后扩展:6。

对于每个质因数来说:

2: (1, 2), (2, 2)

3: (1, 3), (3, 3)

两两乘起来之后发现:少了一项(2, 3),这是由于我们用的是无序数对。那么改成有序数对试试。

 2:        3:

(1, 2)    (1, 3)

(2, 1)    (3, 1)

(2, 2)    (3, 3)

交叉相乘:

(1, 6) (3, 2) (3, 6)

(2, 3) (6, 1) (6, 3)

(2, 6) (6, 2) (6, 6)

发现所有无序数对除了(6, 6)之外都出现了两次,于是补上一个(6, 6)之后/2即可。

现在考虑如何求交叉相乘的表。

设我们的两列数对如下:

(a, b)    (e, f)

(c, d)    (g, h)

要求的式子:

ae + bf + ag + bh + ce + df + cg + dh

因式分解:

(a + c)(e + g) + (b + d)(f + h)

考虑我们一开始列出来的某一列:

2:

(1, 2)

(2, 1)

(2, 2)

可得:a + c = b + d

故上式 = 2(a + c)(e + g)

推广:

设n = Πaipi,则ans = 2Π{getQ(1, a[i], p[i] + 1) + pi * aipi}

复杂度TmlogV,其中V为指数值域。

 1 #include <cstdio>
 2 
 3 typedef long long LL;
 4 const int N = 17;
 5 const LL MO = 1e9 + 7;
 6 
 7 int a[N], p[N];
 8 
 9 inline LL qpow(LL a, LL b) {
10     LL ans = 1;
11     while(b) {
12         if(b & 1) {
13             ans = ans * a % MO;
14         }
15         a = a * a % MO;
16         b = b >> 1;
17     }
18     return ans;
19 }
20 
21 inline LL getQ(LL a, LL q, LL n) {
22     a %= MO;
23     if(!n) {
24         return 0ll;
25     }
26     if(n == 1 || !a || !q) {
27         return a;
28     }
29     if(n == 2) {
30         return (a + a * q % MO) % MO;
31     }
32     if(n & 1) {
33         return (a + getQ(a * q % MO, q, n - 1)) % MO;
34     }
35     return (qpow(q, n >> 1) + 1) * getQ(a, q, n >> 1) % MO;
36 }
37 
38 inline void solve() {
39     int n;
40     scanf("%d", &n);
41     for(int i = 1; i <= n; i++) {
42         scanf("%d%d", &a[i], &p[i]);
43     }
44     LL ans = 2, sum = 1;
45     for(int i = 1; i <= n; i++) {
46         LL temp = qpow(a[i], p[i]) * p[i] % MO;
47         (temp += getQ(1, a[i], p[i] + 1)) %= MO;
48         ans = ans * temp % MO;
49         (sum *= qpow(a[i], p[i])) %= MO;
50         //printf("temp : %lld \n", temp);
51     }
52     //printf("sum = %lld \n", sum);
53     ans = (ans + sum + sum) % MO;
54     ans = ans * ((MO + 1) >> 1) % MO;
55     printf("%lld\n", ans);
56     return;
57 }
58 
59 int main() {
60 
61     int T;
62     scanf("%d", &T);
63     for(int i = 1; i <= T; i++) {
64         printf("Case %d: ", i);
65         solve();
66     }
67 
68     return 0;
69 }
AC代码

题外话:这东西是我在某个最小割练习题表里面看到的......

posted @ 2018-12-12 20:18  huyufeifei  阅读(220)  评论(0编辑  收藏  举报
试着放一个广告栏(虽然没有一分钱广告费)

『Flyable Heart 応援中!』 HHG 高苗京铃 闪十PSS 双六 電動伝奇堂 章鱼罐头制作组 はきか 祝姬 星降夜