一道狄利克雷卷积模板题的组合做法

来源:互联网 发布:精准医疗大数据平台 编辑:程序博客网 时间:2024/05/22 10:35

一道狄利克雷卷积模板题的组合做法


题目描述:

给定f(x)[1,n]上的取值,求函数:

g(x)=i1|xi2|i1iK|iK1f(iK)

[1,n]上的取值。

狄利克雷卷积做法

容易发现,g=1k×f,直接用狄利克雷卷积快速幂即可。

组合做法

显然,p的所有因子在整除关系|下构成一个偏序,可以等价地转化为一张带自环而无其他环的图。则从i1=p开始的序列i1,i2,,ik与有向无环图上从p开始长度为k的路径一一对应。

考虑求从p开始长度为k的路径所有终点位置的和h[p][k],容易写出方程:

h[p][k]=i|ph[i][k1]h[p][1]=dat[p]

然而这个dp的状态规模是O(n2)的,考虑原因:存在很长的链是因为可以多次在一个数上重复(即在自环上走)。设H[p][k]为长度为从p开始长度为k的所有不经过自环的终点位置的和,则:

H[p][k]=i|p,i<pH[i][k]

显然klgK,现在向路径上插入自环。考虑H[n][p]h[n][K]的贡献,pK。需要在p个位置上插入Kp个自环,用隔板法分析可知为:(p+(Kp)1p1)=(K1p1),因此:

h[n][K]=pH[n][p](K1p1)

而答案g(n)等于n的所有因子的h[n][K]的和,即:

g(n)=i|nh[i][K]

自此问题得以解决。复杂度O(nlg2n)

#include <bits/stdc++.h>using namespace std;const int MAXN = 100005;struct node {    int to, next;} edge[MAXN*20];int head[MAXN], top = 0; inline void push(int i, int j){ edge[++top] = (node){j, head[i]}, head[i] = top; }int n, k;void init(){    for (register int i = 1; i <= 100000; i++)        for (register int j = 2; j*i <= 100000; j++)            push(j*i, i);}long long f[MAXN][20], a[MAXN];const long long mod = 1000000007ll;long long fac[MAXN], inv[MAXN], g[MAXN], g2[MAXN];long long power(int a, long long n){    if (n == 0) return 1;    long long b = a, ans = 1;    for (int k = 0; k <= 30; k++) {        if (n&(1ll<<k)) (ans *= b) %= mod;        (b *= b) %= mod;    }    return ans;}inline long long choose(int K, int k){ return fac[K]*inv[k]%mod*inv[K-k]%mod; }void init_c(){       inv[0] = 1;    for (int i = 1; i <= 100000; i++) inv[i] = (inv[i-1]*power(i, mod-2))%mod;    fac[0] = 1;    for (int i = 1; i <= 100000; i++) (fac[i] = fac[i-1]*i) %= mod;}void dp(){    scanf("%d%d", &n, &k);    for (int i = 1; i <= n; i++) scanf("%lld", &f[i][1]);    for (int j = 2; j <= 17; j++)        for (register int i = 1; i <= n; i++) {            f[i][j] = 0;            for (register int k = head[i]; k; k = edge[k].next) {                int to = edge[k].to;                (f[i][j] += f[to][j-1]) %= mod;            }        }    for (register int i = 1; i <= n; i++) {        long long ans = 0;        for (register int j = 1; j <= 17; j++)            (ans += f[i][j]*choose(k-1, j-1)%mod) %= mod;        g[i] = ans;    }// cerr << endl;    memset(g2, 0, sizeof g2);    for (register int i = 1; i <= n; i++)         for (register int j = 1; j*i <= n; j++)            (g2[j*i] += g[i]) %= mod;    for (int i = 1; i <= n; i++)        printf("%lld ", g2[i]);    puts("");}int main(){    freopen("b.in", "r" , stdin);    freopen("b.out", "w", stdout);    init();    init_c();    int T; scanf("%d", &T);    while (T--) dp();    return 0;}
原创粉丝点击