卧薪尝胆,厚积薄发。
b
Date: Sat Oct 20 13:03:14 CST 2018
In Category:
NoCategory
Description:
给定
$ n $
个正整数序列
$a_1,a_2,\dots,a_n$
,每个序列长度为
$ m$
。 选择至少
$ 1 $
个序列,在每个被选择的序列中选择一个元素,求出所有被选择的元素的
$\gcd$
,求所有方案的结果之和,
$1\leqslant n\leqslant 20,1\leqslant m\leqslant 10^5,1\leqslant a_i\leqslant10^5$
Solution:
既然
$a_i$
这么小,可以对每一行开一个桶记录一下每个权值的出现次数,然后再
$O(n\ 10^5\ln 10^5)$
得把每个数的倍数累加到这个数上来,这样枚举每个数作为
$\gcd$
,那么在每个序列中都可以选它的倍数,这样就把
$cnt[k][i]+1$
累乘起来即可,加一是因为可以不选,最后还要减一,是因为减掉一个都不选的情况,发现有的
$\gcd$
可能其实是当前枚举的数的倍数,所以再均摊
$O(n\ln n)$
的复杂度内减掉它的倍数的值即可。
Code:
#include<algorithm>
#include<iostream>
#include<cstdlib>
#include<cstdio>
#include<cmath>
#include<cctype>
#include<cstring>
using namespace std;
inline int read()
{
register int res = 0,f = 1;
register char c = getchar();
while(!isdigit(c))
{
if(c == '-')f = -1;
c = getchar();
}
while(isdigit(c))
{
res = (res << 1) + (res << 3) + c - '0';
c = getchar();
}
return res * f;
}
int n,m;
#define MAXN 21
#define MAXM 100010
int a[MAXN][MAXM];
int cnt[MAXN][MAXM];
#define MAX 100000
int ans[MAX];
#define MOD 1000000007
int main()
{
freopen("b.in","r",stdin);
freopen("b.out","w",stdout);
int res = 0;
scanf("%d%d",&n,&m);
for(int i = 1;i <= n;++i)
for(int j = 1;j <= m;++j)
a[i][j] = read();
for(int i = 1;i <= n;++i)
for(int j = 1;j <= m;++j)
cnt[i][a[i][j]] = (cnt[i][a[i][j]] + 1) % MOD;
for(int k = 1;k <= n;++k)
for(int i = 1;i <= MAX;++i)
for(int j = i + i;j <= MAX;j += i)
cnt[k][i] = (cnt[k][i] + cnt[k][j]) % MOD;
for(int i = MAX;i >= 1;--i)
{
ans[i] = 1;
for(int k = 1;k <= n;++k)
ans[i] = 1ll * ans[i] * (cnt[k][i] + 1) % MOD;
for(int j = i + i;j <= MAX;j += i)
ans[i] = (ans[i] - ans[j] + MOD) % MOD;
ans[i] = (ans[i] - 1 + MOD) % MOD;
}
for(int i = 1;i <= MAX;++i)
res = (res + 1ll * i * ans[i] % MOD) % MOD;
cout << res << endl;
fclose(stdin);
fclose(stdout);
return 0;
}
In tag:
玄学
Copyright © 2020
wjh15101051
ღゝ◡╹)ノ♡