学习笔记单位根反演


##### 基本柿子


frac{1}{n} sum_{i=0}^{n-1} w_n^{ki} = [n|k]



证明如下:


1.当n|k

时,设k=np


frac{1}{n} sum_{i=0}^{n-1} w_{n}^{npi} = frac{1}{n} sum_{i=0}^{n-1} w_{n}^{0} = 1


2.当n
mid k


frac{1}{n} frac{w_n^{nk}-w_{n}^0}{w_n^k-1}=0



##### 例题


BZOJ PYXFIB


sum_{i=0}^n inom{n}{i} F_i [i\%d==0]


frac{1}{d} sum_{i=0}^n inom{n}{i} F_i sum_{j=0}^{d-1} w_d^{ij}


显然F

要用矩乘加速


然后把w_d^j

直接扔进去好了x


最后就是frac{1}{d}(w_n^j F + I)^n


代码



1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
//Love and Freedom.
#include<cmath>
#include<cstdio>
#include<cstring>
#include<algorithm>
#define ll long long
#define inf 20021225
using namespace std;
ll read()
{
    ll s=0,f=1; char ch=getchar();
    while(ch<'0'||ch>'9'){if(ch=='-') f=-1; ch=getchar();}
    while(ch>='0'&&ch<='9') s=s*10+ch-'0',ch=getchar();
    return f*s;
}
struct mat
{
    int a[2][2];
    mat(){}
    mat(int lu,int ru,int ld,int rd){a[0][0]=lu,a[0][1]=ru,a[1][0]=ld,a[1][1]=rd;}
    void clear(){memset(a,0,sizeof(a));}
    void det(){clear(); a[0][0]=a[1][1]=1;}
};
int G,mdn;
void upd(int &x,int y){x+=x+y>=mdn?y-mdn:y;}
mat operator+(mat a,mat b)
{
    mat tmp;
    for(int i=0;i<2;i++)    for(int j=0;j<2;j++)
        tmp.a[i][j]=(a.a[i][j]+b.a[i][j])%mdn;
    return tmp;
}
mat operator*(mat a,mat b)
{
    mat tmp; tmp.clear();
    for(int i=0;i<2;i++)    for(int j=0;j<2;j++)
        for(int k=0;k<2;k++)    upd(tmp.a[i][j],1ll*a.a[i][k]*b.a[k][j]%mdn);
    return tmp;
}
int fac[211],tot;
int ksm(int bs,int mi)
{
    int ans=1;
    while(mi)
    {
        if(mi&1)    ans=1ll*ans*bs%mdn;
        bs=1ll*bs*bs%mdn; mi>>=1;
    }
    return ans;
}
void getG()
{
    int top=mdn-1; tot=0;
    for(int i=1;i<=sqrt(top);i++)
        if(top%i==0)    fac[++tot]=i,fac[++tot]=top/i;
    for(G=2;G<=100;G++)
    {
        int i;
        for(i=1;i<=tot;i++)    if(fac[i]!=top&&ksm(G,fac[i])==1)    break;
        if(i>tot)    return;
    }
}
mat mksm(mat bs,ll mi)
{
    mat ans; ans.det();
    while(mi)
    {
        if(mi&1)    ans=ans*bs;
        bs=bs*bs; mi>>=1;
    }
    return ans;
}
int main()
{
    int T=read();
    while(T--)
    {
        ll n=read(),k=read(); mdn=read(); getG(); int Wn=ksm(G,(mdn-1)/k); int ans=0;
        for(int i=0;i<k;i++)
        {
            int w=ksm(Wn,i); mat cur=mksm(mat(w+1,w,w,1),n);
            int val=cur.a[0][0];
            upd(ans,val);
        }
        printf("%d
",1ll*ans*ksm(k,mdn-2)%mdn);
    }
    return 0;
}

View Code



LOJ6485. LJJ 学二项式定理


frac{1}{4}sum_{i=0}^n sum_{j=0}^3 a_j s^i inom{n}{i} [i\%4==j]


frac{1}{4}sum_{i=0}^nsum_{j=0}^3 a_j s^iinom{n}{i} [(i-j)\%4==0]


frac{1}{4}sum_{i=0}^nsum_{j=0}^3 a_j s^iinom{n}{i} sum_{k=0}^3 w_4^{(i-j)k}


frac{1}{4}sum_{j=0}^3 a_j sum_{i=0}^n s^i inom{n}{i} sum_{k=0}^3 w_{4}^{(i-j)k}


frac{1}{4}sum_{j=0}^3 sum_{k=0}^3 a_j w^{-jk}(sw_{4}^k+1)^n


然后就做完惹



1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
//Love and Freedom.
#include<cmath>
#include<cstdio>
#include<cstring>
#include<algorithm>
#define ll long long
#define inf 20021225
#define mdn 998244353
#define G 3
#define inv4 748683265
using namespace std;
ll read()
{
    ll s=0,f=1; char ch=getchar();
    while(ch<'0'||ch>'9'){if(ch=='-') f=-1; ch=getchar();}
    while(ch>='0'&&ch<='9') s=s*10+ch-'0',ch=getchar();
    return f*s;
}
int ksm(int bs,ll mi)
{
    int ans=1;
    while(mi)
    {
        if(mi&1)    ans=1ll*ans*bs%mdn;
        bs=1ll*bs*bs%mdn; mi>>=1;
    }
    return ans;
}
int a[4];
int main()
{
    int Wn=ksm(G,(mdn-1)/4);
    int T=read();
    while(T--)
    {
        ll n=read(); int s=read(); int ans=0;
        for(int i=0;i<4;i++)
        {
            int w=Wn,cur=0; a[i]=read();
            for(int j=0,k=1;j<4;j++,k=1ll*k*w%mdn)
                (cur+=1ll*ksm(1ll*s*k%mdn+1,n)*ksm(k,4-i)%mdn*inv4%mdn)%=mdn;
            (ans+=1ll*cur*a[i]%mdn)%=mdn;
        }
        printf("%d
",ans);
    }
    return 0;
}

View Code