数论进阶课

5.1 莫比乌斯反演和积性函数

在这里插入图片描述

#include <iostream>
#include <algorithm>

using namespace std;

typedef long long LL;

const int N = 50010;

int T, a, b, c, d, k;
int primes[N], cnt, mu[N], smu[N];
bool st[N];

void prework()   //预处理莫比乌斯函数和前缀和(分块计算时要用到区间和)
{
    mu[1] = 1;
    for (int i = 2; i < N; i ++ )
    {
        if (!st[i]) primes[cnt ++ ] = i, mu[i] = -1;
        for (int j = 0; primes[j] * i < N; j ++ )
        {
            st[primes[j] * i] = true;
            if (i % primes[j] == 0) break;
            mu[primes[j] * i] = -mu[i];
        }
    }
    for (int i = 1; i < N; i ++ ) smu[i] = smu[i - 1] + mu[i];
}
int g(int i, int n) //数论分块的右端点
{
    return n / (n / i);
}
LL f(int a, int b)
{
    a = a / k, b = b / k;   //对应到推公式里的a',b'换元
    LL res = 0;
    int n = min(a, b);
    for (int l = 1, r; l <= n; l = r + 1)
    {
        r = min({n, g(l, a), g(l, b)});
        int _d = l;
        res += (LL) (smu[r] - smu[l - 1]) * (a / _d) * (b / _d);
    }
    return res;
}
void solve()
{
    scanf("%d%d%d%d%d", &a, &b, &c, &d, &k);
    printf("%lld\n", f(b, d) - f(a - 1, d) - f(b, c - 1) + f(a - 1, c - 1));
}
int main()
{
    prework();
    scanf("%d", &T);
    while (T -- ) solve();
    return 0;
}

1358. 约数个数和(莫比乌斯反演经典、双上限整除分块)

在这里插入图片描述

在这里插入图片描述
在这里插入图片描述

#include<bits/stdc++.h>
using namespace std;
const int N = 5e4+10;
int primes[N],h[N],cnt,sum[N],mu[N],st[N];
int g(int k,int x){
    return k/(k/x);
}
void init(){
    mu[1]=1;
    for(int i=2;i<N;i++){
        if(!st[i])primes[cnt++]=i,mu[i]=-1;
        for(int j=0;primes[j]*i<N;j++){
            st[primes[j]*i]=1;
            if(i%primes[j]==0)break;
            mu[primes[j]*i]=-mu[i];
        }
    }
    for(int i=1;i<N;i++)sum[i]=sum[i-1]+mu[i];
    for(int i=1;i<N;i++){
        for(int l=1,r;l<=i;l=r+1){
            r=min(i,g(i,l));
            h[i]+=(r-l+1)*(i/l);
        }
    }
}


signed main(){
    init();
    int T;
    cin>>T;
    while(T--){
        int n,m;
        cin>>n>>m;
        int k=min(n,m);
        long long ans=0;
        for(int l=1,r;l<=k;l=r+1){
            r=min(k,min( g(n,l),g(m,l) ));
            ans+=(long long)(sum[r]-sum[l-1]) * h[n/l] *h[m/l];
        }
        printf("%lld\n",ans);
    }
}

积性函数

常见积性函数有
在这里插入图片描述
在这里插入图片描述

221. 龙哥的问题

#include <iostream>
#include <cstring>
#include <algorithm>

using namespace std;

typedef long long LL;

int main()
{
    int n;
    cin >> n;
    LL res = n;
    for (int i = 2; i <= n / i; i ++ )
        if (n % i == 0)
        {
            int a = 0, p = i;
            while (n % p == 0) a ++, n /= p;
            res = res * (p + (LL)a * p - a) / p;
        }
    if (n > 1) res = res * ((LL)n + n - 1) / n;
    cout << res << endl;
    return 0;
}

BSGS

朴素版(a,p)=1
非朴素版 (a,p)!=1
需要用到扩展欧几里得求逆元

3124. BSGS

在这里插入图片描述

#include <iostream>
#include <cstring>
#include <algorithm>
#include <cmath>
#include <unordered_map>

using namespace std;

typedef long long LL;

int bsgs(int a, int b, int p)
{
    if (1 % p == b % p) return 0;
    int k = sqrt(p) + 1;
    unordered_map<int, int> hash;
    for (int i = 0, j = b % p; i < k; i ++ )
    {
        hash[j] = i;
        j = (LL)j * a % p;
    }
    int ak = 1;
    for (int i = 0; i < k; i ++ ) ak = (LL)ak * a % p;

    for (int i = 1, j = ak; i <= k; i ++ )
    {
        if (hash.count(j)) return (LL)i * k - hash[j];
        j = (LL)j * ak % p;
    }
    return -1;
}

int main()
{
    int a, p, b;
    while (cin >> a >> p >> b, a || p || b)
    {
        int res = bsgs(a, b, p);
        if (res == -1) puts("No Solution");
        else cout << res << endl;
    }
    return 0;
}

在这里插入图片描述

#include <iostream>
#include <cstring>
#include <algorithm>
#include <cmath>
#include <unordered_map>

using namespace std;

typedef long long LL;
const int INF = 1e8;

int exgcd(int a, int b, int& x, int& y)
{
    if (!b)
    {
        x = 1, y = 0;
        return a;
    }
    int d = exgcd(b, a % b, y, x);
    y -= a / b * x;
    return d;
}

int bsgs(int a, int b, int p)
{
    if (1 % p == b % p) return 0;
    int k = sqrt(p) + 1;
    unordered_map<int, int> hash;
    for (int i = 0, j = b % p; i < k; i ++ )
    {
        hash[j] = i;
        j = (LL)j * a % p;
    }
    int ak = 1;
    for (int i = 0; i < k; i ++ ) ak = (LL)ak * a % p;
    for (int i = 1, j = ak; i <= k; i ++ )
    {
        if (hash.count(j)) return i * k - hash[j];
        j = (LL)j * ak % p;
    }
    return -INF;
}

int exbsgs(int a, int b, int p)
{
    b = (b % p + p) % p;
    if (1 % p == b % p) return 0;
    int x, y;
    int d = exgcd(a, p, x, y);
    if (d > 1)
    {
        if (b % d) return -INF;
        exgcd(a / d, p / d, x, y);
        //用扩展欧几里得算法求a/d和p/d的逆元 (也就是x)
        p/=d;
        return exbsgs(a, (LL)b / d * x % p, p) + 1;
    }
    return bsgs(a, b, p);
}

int main()
{
    int a, p, b;
    while (cin >> a >> p >> b, a || p || b)
    {
        int res = exbsgs(a, b, p);
        if (res < 0) puts("No Solution");
        else cout << res << endl;
    }
    return 0;
}

2526. 随机数生成器

在这里插入图片描述

#include <iostream>
#include <cstring>
#include <algorithm>
#include <cmath>
#include <unordered_map>

using namespace std;

typedef long long LL;

int qmi(int a, int b, int p)
{
    int res = 1;
    while (b)
    {
        if (b & 1) res = (LL)res * a % p;
        a = (LL)a * a % p;
        b >>= 1;
    }
    return res;
}

int inv(int a, int p)
{
    return qmi(a, p - 2, p);
}

int exgcd(int a, int b, int& x, int& y)
{
    if (!b)
    {
        x = 1, y = 0;
        return a;
    }
    int d = exgcd(b, a % b, y, x);
    y -= a / b * x;
    return d;
}

int bsgs(int a, int b, int p)
{
    if (1 % p == b % p) return 0;
    int k = sqrt(p) + 1;
    unordered_map<int, int> hash;
    for (int i = 0, j = b % p; i < k; i ++ )
    {
        hash[j] = i;
        j = (LL)j * a % p;
    }
    int ak = 1;
    for (int i = 0; i < k; i ++ ) ak = (LL)ak * a % p;
    for (int i = 1, j = ak; i <= k; i ++ )
    {
        if (hash.count(j)) return (LL)i * k - hash[j];
        j = (LL)j * ak % p;
    }
    return -2;
}

int main()
{
    int T;
    cin >> T;
    while (T -- )
    {
        int p, a, b, x1, t;
        cin >> p >> a >> b >> x1 >> t;
        if (a == 0)
        {
            if (x1 == t) puts("1");
            else if (b == t) puts("2");
            else puts("-1");
        }
        else if (a == 1)
        {
            if (b == 0) puts(t == x1 ? "1" : "-1");
            else
            {
                int x, y;
                exgcd(b, p, x, y);
                x = ((LL)x * (t - x1) % p + p) % p;
                cout << x + 1 << endl;
            }
        }
        else
        {
            int C = (LL)b * inv(a - 1, p) % p;
            int A = (x1 + C) % p;
            if (A == 0)
            {
                int u = (-C + p) % p;
                puts(u == t ? "1" : "-1");
            }
            else
            {
                int B = (t + C) % p;
                cout << bsgs(a, (LL)B * inv(A, p) % p, p) + 1 << endl;
            }
        }
    }
    return 0;
}

FFT

快速傅里叶变换

3122. 多项式乘法

在这里插入图片描述

#include <iostream>
#include <cstring>
#include <algorithm>
#include <cmath>

using namespace std;

const int N = 300010;
const double PI = acos(-1);

int n, m;
struct Complex
{
    double x, y;
    Complex operator+ (const Complex& t) const
    {
        return {x + t.x, y + t.y};
    }
    Complex operator- (const Complex& t) const
    {
        return {x - t.x, y - t.y};
    }
    Complex operator* (const Complex& t) const
    {
        return {x * t.x - y * t.y, x * t.y + y * t.x};
    }
}a[N], b[N];
int rev[N], bit, tot;

void fft(Complex a[], int inv)
{
    for (int i = 0; i < tot; i ++ )
        if (i < rev[i])
            swap(a[i], a[rev[i]]);
    for (int mid = 1; mid < tot; mid <<= 1)
    {
        auto w1 = Complex({cos(PI / mid), inv * sin(PI / mid)});
        for (int i = 0; i < tot; i += mid * 2)
        {
            auto wk = Complex({1, 0});
            for (int j = 0; j < mid; j ++, wk = wk * w1)
            {
                auto x = a[i + j], y = wk * a[i + j + mid];
                a[i + j] = x + y, a[i + j + mid] = x - y;
            }
        }
    }
}

int main()
{
    scanf("%d%d", &n, &m);
    for (int i = 0; i <= n; i ++ ) scanf("%lf", &a[i].x);
    for (int i = 0; i <= m; i ++ ) scanf("%lf", &b[i].x);
    while ((1 << bit) < n + m + 1) bit ++;
    tot = 1 << bit;
    for (int i = 0; i < tot; i ++ )
        rev[i] = (rev[i >> 1] >> 1) | ((i & 1) << (bit - 1));
    fft(a, 1), fft(b, 1);
    for (int i = 0; i < tot; i ++ ) a[i] = a[i] * b[i];
    fft(a, -1);
    for (int i = 0; i <= n + m; i ++ )
        printf("%d ", (int)(a[i].x / tot + 0.5));

    return 0;
}

3123. 高精度乘法II

在这里插入图片描述

#include <iostream>
#include <cstring>
#include <algorithm>
#include <cmath>

using namespace std;

const int N = 300000;
const double PI = acos(-1);

struct Complex
{
    double x, y;
    Complex operator+ (const Complex& t) const
    {
        return {x + t.x, y + t.y};
    }
    Complex operator- (const Complex& t) const
    {
        return {x - t.x, y - t.y};
    }
    Complex operator* (const Complex& t) const
    {
        return {x * t.x - y * t.y, x * t.y + y * t.x};
    }
}a[N], b[N];
char s1[N], s2[N];
int res[N];
int rev[N], bit, tot;

void fft(Complex a[], int inv)
{
    for (int i = 0; i < tot; i ++ )
        if (i < rev[i])
            swap(a[i], a[rev[i]]);
    for (int mid = 1; mid < tot; mid *= 2)
    {
        auto w1 = Complex({cos(PI / mid), inv * sin(PI / mid)});
        for (int i = 0; i < tot; i += mid * 2)
        {
            auto wk = Complex({1, 0});
            for (int j = 0; j < mid; j ++, wk = wk * w1)
            {
                auto x = a[i + j], y = wk * a[i + j + mid];
                a[i + j] = x + y, a[i + j + mid] = x - y;
            }
        }
    }
}

int main()
{
    scanf("%s%s", s1, s2);
    int n = strlen(s1) - 1, m = strlen(s2) - 1;
    for (int i = 0; i <= n; i ++ ) a[i].x = s1[n - i] - '0';
    for (int i = 0; i <= m; i ++ ) b[i].x = s2[m - i] - '0';
    while ((1 << bit) < n + m + 1) bit ++ ;
    tot = 1 << bit;
    for (int i = 0; i < tot; i ++ )
        rev[i] = ((rev[i >> 1] >> 1)) | ((i & 1) << (bit - 1));
    fft(a, 1), fft(b, 1);
    for (int i = 0; i < tot; i ++ ) a[i] = a[i] * b[i];
    fft(a, -1);

    int k = 0;
    for (int i = 0, t = 0; i < tot || t; i ++ )
    {
        t += a[i].x / tot + 0.5;
        res[k ++ ] = t % 10;
        t /= 10;
    }

    while (k > 1 && !res[k - 1]) k -- ;
    for (int i = k - 1; i >= 0; i -- ) printf("%d", res[i]);

    return 0;
}
#include <iostream>
#include <cstring>
#include <algorithm>
#include <cmath>

using namespace std;

const int N = 300010;
const double PI = acos(-1);

int n, m;
struct Complex
{
    double x, y;
    Complex operator+ (const Complex& t) const
    {
        return {x + t.x, y + t.y};
    }
    Complex operator- (const Complex& t) const
    {
        return {x - t.x, y - t.y};
    }
    Complex operator* (const Complex& t) const
    {
        return {x * t.x - y * t.y, x * t.y + y * t.x};
    }
}a[N], b[N];
int c[N];
int rev[N], bit, tot;

void fft(Complex a[], int inv)
{
    for (int i = 0; i < tot; i ++ )
        if (i < rev[i])
            swap(a[i], a[rev[i]]);
    for (int mid = 1; mid < tot; mid <<= 1)
    {
        auto w1 = Complex({cos(PI / mid), inv * sin(PI / mid)});
        for (int i = 0; i < tot; i += mid * 2)
        {
            auto wk = Complex({1, 0});
            for (int j = 0; j < mid; j ++, wk = wk * w1)
            {
                auto x = a[i + j], y = wk * a[i + j + mid];
                a[i + j] = x + y, a[i + j + mid] = x - y;
            }
        }
    }
}

int main()
{
    string A,B;
    cin>>A>>B;
    n=A.length()-1;
    m=B.length()-1;
    //scanf("%d%d", &n, &m);
    //for (int i = 0; i <= n; i ++ ) scanf("%lf", &a[i].x);
    //for (int i = 0; i <= m; i ++ ) scanf("%lf", &b[i].x);
    for(int i=0;i<=n;i++)a[(n-i)].x=A[i]-'0';
    for(int j=0;j<=m;j++)b[(m-j)].x=B[j]-'0';
    while ((1 << bit) < n + m + 1) bit ++;
    tot = 1 << bit;
    for (int i = 0; i < tot; i ++ )
        rev[i] = (rev[i >> 1] >> 1) | ((i & 1) << (bit - 1));
    fft(a, 1), fft(b, 1);
    for (int i = 0; i < tot; i ++ ) a[i] = a[i] * b[i];
    fft(a, -1);
    
    for (int i = 0; i <= n + m; i ++ ){
        //printf("%d ", (int)(a[i].x / tot + 0.5));
        c[i]=(int) (a[i].x/tot+0.5);
    }
    int k=n+m+1000;
    //现在要对系数处理,如果c数组某个系数大于10就进1
    for(int i=0;i<=k;i++){
        if(c[i]>10)c[i+1]+= (c[i]/10),c[i]%=10;
    }
    while(k>1&&c[k]==0)k--;
    for(int i=k;i>=0;i--)printf("%d",c[i]);
    //for(int i=0;i<=k;i++)printf("%d ",c[i]);
}

2241. 礼物

在这里插入图片描述先对式子化简
实际上就是求a和b的卷积
考虑到可以旋转,所以将a反转,b破链成环
最后得到的a序列的【n ,2n-1】最大值就是答案
这个是选择枚举c【-m,m】

#include <iostream>
#include <cstring>
#include <algorithm>
#include <cmath>

using namespace std;

const int N = 300010;
const double PI = acos(-1);

int n, m;
struct Complex
{
    double x, y;
    Complex operator+ (const Complex& t) const
    {
        return {x + t.x, y + t.y};
    }
    Complex operator- (const Complex& t) const
    {
        return {x - t.x, y - t.y};
    }
    Complex operator* (const Complex& t) const
    {
        return {x * t.x - y * t.y, x * t.y + y * t.x};
    }
}a[N], b[N];
int cc[N];
int rev[N], bit, tot;

void fft(Complex a[], int inv)
{
    for (int i = 0; i < tot; i ++ )
        if (i < rev[i])
            swap(a[i], a[rev[i]]);
    for (int mid = 1; mid < tot; mid <<= 1)
    {
        auto w1 = Complex({cos(PI / mid), inv * sin(PI / mid)});
        for (int i = 0; i < tot; i += mid * 2)
        {
            auto wk = Complex({1, 0});
            for (int j = 0; j < mid; j ++, wk = wk * w1)
            {
                auto x = a[i + j], y = wk * a[i + j + mid];
                a[i + j] = x + y, a[i + j + mid] = x - y;
            }
        }
    }
}
int suma,sumb,ans;
int maxx=-0x3f3f3f3f;
int minn= 0x3f3f3f3f;
int main()
{
    scanf("%d%d", &n, &m);
    for (int i = 0; i < n; i ++ ) scanf("%lf", &a[i].x),suma+=a[i].x;
    for (int i = 0; i < n; i ++ ) scanf("%lf", &b[i].x),sumb+=b[i].x,b[i+n].x=b[i].x;
    //求c
    //int c=(sumb-suma)/n;
    //cout<<c<<endl;
    //if(c>0)for(int i=0;i<n;i++)a[i].x+=c;
    //if(c<0)for(int i=0;i<n;i++)b[i].x-=c,b[i+n].x=b[i].x;
    
    for(int i=0;i<n;i++)ans+=a[i].x*a[i].x+b[i].x*b[i].x;
    //for(int i=0;i<n;i++)printf("%lf ",a[i].x);
    reverse(a,a+n);
    //printf("\n");
    //for(int i=0;i<n;i++)printf("%lf ",a[i].x);
    while ((1 << bit) < n + 2*n + 1) bit ++;
    tot = 1 << bit;
    for (int i = 0; i < tot; i ++ )
        rev[i] = (rev[i >> 1] >> 1) | ((i & 1) << (bit - 1));
    fft(a, 1), fft(b, 1);
    for (int i = 0; i < tot; i ++ ) a[i] = a[i] * b[i];
    fft(a, -1);
    for (int i = 0; i <= n + n*2; i ++ )
        //printf("%d ", (int)(a[i].x / tot + 0.5)),
        cc[i]=(int) (a[i].x / tot + 0.5);
    for(int k=0;k<n;k++)maxx=max(maxx , cc[k+n]);
    for(int c=-m;c<=m;c++)minn=min(minn , n*c*c + 2*c*(suma-sumb));
    cout<<ans-2*maxx+minn;
}

不枚举c求对称轴,某些边界未特判所以17/20
考虑到求对称轴之所以错是因为整除的话
-b/2a未必是最小的点
所以再运算一下c+1和c-1

#include <iostream>
#include <cstring>
#include <algorithm>
#include <cmath>

using namespace std;

const int N = 300010;
const double PI = acos(-1);

int n, m;
struct Complex
{
    double x, y;
    Complex operator+ (const Complex& t) const
    {
        return {x + t.x, y + t.y};
    }
    Complex operator- (const Complex& t) const
    {
        return {x - t.x, y - t.y};
    }
    Complex operator* (const Complex& t) const
    {
        return {x * t.x - y * t.y, x * t.y + y * t.x};
    }
}a[N], b[N];
int cc[N];
int rev[N], bit, tot;

void fft(Complex a[], int inv)
{
    for (int i = 0; i < tot; i ++ )
        if (i < rev[i])
            swap(a[i], a[rev[i]]);
    for (int mid = 1; mid < tot; mid <<= 1)
    {
        auto w1 = Complex({cos(PI / mid), inv * sin(PI / mid)});
        for (int i = 0; i < tot; i += mid * 2)
        {
            auto wk = Complex({1, 0});
            for (int j = 0; j < mid; j ++, wk = wk * w1)
            {
                auto x = a[i + j], y = wk * a[i + j + mid];
                a[i + j] = x + y, a[i + j + mid] = x - y;
            }
        }
    }
}
int suma,sumb,ans;
int maxx=-0x3f3f3f3f;
int minn= 0x3f3f3f3f;
int cal2(int k){
    int res=0;
    for(int i=0;i<n;i++)res+=cc[i+k];
    return res;
}
int cal(int k){
    int res=0;
    for(int i=0;i<n;i++)res+=cc[i+k];
    return res;
}
int main()
{
    scanf("%d%d", &n, &m);
    for (int i = 0; i < n; i ++ ) scanf("%lf", &a[i].x);
    for (int i = 0; i < n; i ++ ) scanf("%lf", &b[i].x),b[i+n].x=b[i].x;
    //求c
    int c=(sumb-suma)/n;
    if(c>0)for(int i=0;i<n;i++)a[i].x+=c;
    if(c<0)for(int i=0;i<n;i++)b[i].x-=c,b[i+n].x=b[i].x;
    
    for(int i=0;i<n;i++)ans+=a[i].x*a[i].x+b[i].x*b[i].x;//求ans
    for(int i=0;i<n;i++)suma+=a[i].x,sumb+=b[i].x;//求suma,sumb
    reverse(a,a+n);
    while ((1 << bit) < n + 2*n + 1) bit ++;
    tot = 1 << bit;
    for (int i = 0; i < tot; i ++ )
        rev[i] = (rev[i >> 1] >> 1) | ((i & 1) << (bit - 1));
    fft(a, 1), fft(b, 1);
    for (int i = 0; i < tot; i ++ ) a[i] = a[i] * b[i];
    fft(a, -1);
    for (int i = 0; i <= n + n*2; i ++ )
        cc[i]=(int) (a[i].x / tot + 0.5);
    
    for(int k=0;k<n;k++)maxx=max(maxx , cc[k+n]);
    minn=min(minn , 0);
    minn=min(minn , n+2* 1*(suma-sumb));
    minn=min(minn , n+2*-1*(suma-sumb));
    cout<<ans-2*maxx+minn;
}

在这里插入图片描述

#include <bits/stdc++.h>
using namespace std;
 
typedef double D;
const int N = 16500;
const D PI = acos(-1.0);
 
struct cox{
    D x, y;
    cox(D xx = 0, D yy = 0){x = xx; y = yy;}
}a[N], b[N];
 
cox operator +(const cox &u, const cox &v){
    return cox(u.x + v.x, u.y + v.y);
}
 
cox operator -(const cox &u, const cox &v){
    return cox(u.x - v.x, u.y - v.y);
}
 
cox operator *(const cox &u, const cox &v){
    return cox(u.x * v.x - u.y * v.y , u.x * v.y + u.y * v.x);
}
 
int n , rnk[N], c[N], len = 16384, cn = 13;
 
void fft(cox *t, int op){
    for (int i = 0; i < len; i++) if (i < rnk[i]) swap(t[i], t[rnk[i]]);
    for (int i = 1; i < len; i <<= 1){
        cox wn(cos(PI / i) , op * sin(PI / i));
        for (int j = 0, jj = i << 1; j < len; j += jj){
            cox w (1, 0);
            for (int k = 0; k < i; k++, w = w * wn){
                cox r = t[j + k], rr = w * t[i + j + k];
                t[j + k] = r + rr;
                t[i + j + k] = r - rr;
            }
        }
    }
}
 
//fft
void po(int op){
    if (op){
        fft(a, 1); fft(b, 1);
        for (int i = 0; i <= len; i++) a[i] = a[i] * b[i];
        for (int i = 0; i <= len; i++) b[i] = b[i] * b[i];
        fft(a, -1); fft(b, -1);
 
        for (int i = 0; i <= len; i++) c[i] = (int)(a[i].x / len + 0.5);
        for (int i = 0; i <= len; i++){
            if (c[i] >= 10) c[i + 1] += c[i] / 10, c[i] %= 10;
        }
        for (int i = 0; i <= len; i++) a[i].x = c[i], a[i].y = 0, c[i] = 0;
        for (int i = 0; i <= len; i++) c[i] = (int)(b[i].x / len + 0.5);
        for (int i = 0; i <= len; i++){
            if (c[i] >= 10) c[i + 1] += c[i] / 10, c[i] %= 10;
        }
        for (int i = 0; i <= len; i++) b[i].x = c[i], b[i].y = 0, c[i] = 0;
    }
    //op=1表示结算a和b
    //op=0表示只结算b
    else{
        fft(b, 1);
        for (int i = 0; i <= len; i++) b[i] = b[i] * b[i];
        fft(b, -1);
 
        for (int i = 0; i <= len; i++) c[i] = (int)(b[i].x / len + 0.5);
        for (int i = 0; i <= len; i++){
            if (c[i] >= 10) c[i + 1] += c[i] / 10, c[i] %= 10;
        }
        for (int i = 0; i <= len; i++) b[i].x = c[i], b[i].y = 0, c[i] = 0;
    }
}
 
int main(){
    scanf("%d", &n);
    if (n == 1) {puts("1"); return 0;}
    if (n == 2) {puts("2"); return 0;}
    if (n % 3 == 1) a[0] = 4, n -= 4;
    else if (n % 3 == 2) a[0] = 2, n -= 2;
    else a[0] = 1;
    b[0] = 3; n /= 3;
    for (int i = 0; i < len; i++) rnk[i] = (rnk[i>>1] >> 1) | ((i&1)<<cn);
    for (; n; n >>= 1) po((n&1)?1:0);
    int i;
    for (i = len; a[i].x == 0;i--);
    printf("%d\n", i+1);
    if (i <= 99) for(; i>= 0;i--) printf("%d", (int)a[i].x);
    else for (int j = 100; j; j--, i--) printf("%d", (int)a[i].x);
    return 0;
}

生成函数

3132. 食物

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

#include<bits/stdc++.h>
using namespace std;
const int N=510,P=10007;
char s[N];
typedef long long ll;
int main(){
    cin>>s;
    ll n=0;
    for(int i=0;s[i];i++)
        n=(n*10+s[i]-'0')%P;//最高次项开始,往外展开,原数除以P的余数:秦九韶算法
    cout<<n*(n+1)*(n+2)/6%P<<endl;
}

burnside引理与polya定理

3133. 串珠子

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

/*
题意简化:给一个有N个珠子的环染色,颜色一共有M种,
旋转或翻转后能够完全重合在一起而且对应位置颜色一致为一种情况,问方案数?

*/
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
int gcd(int a,int b){
    return b?gcd(b,a%b):a;
}
int f(int a,int b){
    int res=1,t=a;
    while(b){
        if(b&1)res=res*t;
        t=t*t;
        b>>=1;
    }
    return res;
}
int main(){
    int m,n;
    while(cin>>m>>n,m||n){
        ll res=0;
        for(int i=0;i<n;i++)res+=f(m,gcd(n,i)); //旋转
        if(n%2)res+=n*f(m,(n+1)/2); //翻转
        else res+=n/2*(f(m,n/2+1)+f(m,n/2));
        cout<<res/n/2<<endl;
    }
}

3134. 魔法手链

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
const int N = 11, P = 9973;
int m;
struct MATRIX {
    int a[N][N];
    MATRIX() { memset(a, 0, sizeof a); }
}; 
MATRIX operator*(MATRIX a, MATRIX b) {
    MATRIX c;
    for (int i = 1; i <= m; i++)
        for (int j = 1; j <= m; j++)
            for (int k = 1; k <= m; k++)
                c.a[i][j] = (c.a[i][j] + a.a[i][k] * b.a[k][j]) % P;
    return c;
}
int qmi(MATRIX a, int b) {
    MATRIX res;
    // 初始化矩阵
    for (int i = 1; i <= m; i++) res.a[i][i] = 1;
    while (b) {
        if (b & 1) res = res * a;
        a = a * a, b >>= 1;
    }
    // 算对角线答案
    int sum = 0;
    for (int i = 1; i <= m; i++) sum += res.a[i][i];
    return sum;
}
int phi(int n) {
    int res = n;
    for (int i = 2; i <= n / i; i++) {
        if (n % i == 0) {
            res = res / i * (i - 1);
            while (n % i == 0) n /= i;
        }
    }
    if (n > 1) res = res / n * (n - 1);
    return res % P;
}
int inv(int n) {
    n %= P;
    for (int i = 1; i < P; i++)
        if (i * n % P == 1) return i;
    return -1;
}
int main() {
    int T, n, k;
    cin >> T;
    while (T-- && cin >> n >> m >> k) {
        MATRIX tr;
        // 构造矩阵
        for (int i = 1; i <= m; i++)
            for (int j = 1; j <= m; j++)
                tr.a[i][j] = 1;
        for (int x, y; k-- && cin >> x >> y; ) 
            tr.a[x][y] = tr.a[y][x] = 0;
        // 枚举 n 的约数算答案
        int res = 0;
        for (int i = 1; i <= n / i; i++) 
            if (n % i == 0) {
                res = (res + qmi(tr, i) * phi(n / i)) % P;
                if (i != n / i) 
                    res = (res + qmi(tr, n / i) * phi(i)) % P;
            }
        // 结尾别忘除以 n
        cout << res * inv(n) % P << endl;
    }
    return 0;
}

斯特林数

3165. 第一类斯特林数

在这里插入图片描述
在这里插入图片描述

#include <iostream>
#include <cstring>
#include <algorithm>

using namespace std;

typedef long long LL;
const int N = 1010, MOD = 1e9 + 7;

int n, m;
int f[N][N];

int main()
{
    cin >> n >> m;
    f[0][0] = 1;
    for (int i = 1; i <= n; i ++ )
        for (int j = 1; j <= m; j ++ )
            f[i][j] = (f[i - 1][j - 1] + (LL)(i - 1) * f[i - 1][j]) % MOD;
    cout << f[n][m] << endl;
    return 0;
}

3166. 第二类斯特林数

在这里插入图片描述

在这里插入图片描述
在这里插入图片描述

#include<bits/stdc++.h>
using namespace std;
const int N = 1010,mod = 1e9+7;
typedef long long LL;
int f[N][N];
int n,m;
int main()
{
    cin>>n>>m;
    f[0][0]=1;
    for(int i=1;i<=n;i++)
    {
        for(int j=1;j<=m;j++)
        {
            f[i][j]=(f[i-1][j-1]+(LL)j*f[i-1][j])%mod;
        }
    }
    cout<<f[n][m]<<endl;
    return 0;
}

3020. 建筑师

在这里插入图片描述

在这里插入图片描述

#include<bits/stdc++.h>
using namespace std;
const int N = 1e5+10;
const int M = 201;
const int mod=1e9+7;
int f[N][M];
int c[M][M];
signed main(){
    //先处理第一类斯特林数
    f[0][0]=1;
    for(int i=1;i<N;i++){
        for(int j=1;j<M;j++){
            f[i][j]=(f[i-1][j-1]+(long long)(i-1)*f[i-1][j])%mod;
        }
    }
    //处理组合数
    for(int i=0;i<M;i++){
        for(int j=0;j<=i;j++){
            if(!j)c[i][j]=1;
            else c[i][j]=(c[i-1][j-1]+c[i-1][j])%mod;
        }
    }
    int T,n,a,b;
    scanf("%d",&T);
    while(T--){
        scanf("%d%d%d",&n,&a,&b);
        printf("%d\n" ,(long long)f[n-1][a+b-2]*c[a+b-2][a-1]%mod);
    }
    
}

3164. 线性基

线性基的定义
对于一个数列 A 来说,P 是他的线性基当且仅当

  1. A任意数字都能通过P中的一些数字异或出来
  2. P只能异或出A中的数或A中的数能异或出来的东西
  3. P中任意数都不能被P中其他数异或出来
  4. P中数最高位不重复

由3容易得出
Pi⊕Pj≠Pk⊕Pl
Pi⊕Pj≠Pk⊕Pl
所以,A中数可以异或出来的最大值就是P中数可以异或出来的最大值

简单来讲,线性基是一种擅长处理异或问题的数据结构.
在这里插入图片描述

210. 异或运算

在这里插入图片描述
它大体有以下几个操作:

1.插入

inline void insert(ll x)
{
    for(int i=MAXN;i>=0;--i)
        if(x&(1ll<<i))
            if(!a[i]){a[i]=x;return;}
            else x^=a[i];
    flag=true;
    return ; 
}
令插入的数为x,考虑x的二进制最高位i,
若线性基的第i位为0,则直接在该位插入x,退出;
若线性基的第i位已经有值,就把x和其异或。

很容易想到,这样得出的线性基a[i]是最高位为i(即1的最高位是i)的异或和。
这样插入,可以得到线性基的异或和的值域与原数列的异或和值域相同。

2.求最大值

inline ll qmax()
{
    ll res=0;
    for(int i=MAXN;i>=0;--i)
        res=max(res,res^a[i]);
    return res;
}
类似贪心的做法,如果x的当前位是0,那么异或一定会更优,否则当前位如果为1,则一定会更不优。

3.求最小值

inline ll qmin()
{
    if(flag) return 0;
    for(int i=0;i<=MAXN;++i)
        if(a[i]) return a[i];
}
没什么可说的。。。。

4.查询x是否在值域中

inline bool check(ll x)
{
    for(int i=MAXN;i>=0;--i)
        if(x&(1ll<<i))
            if(!a[i])   return false;
            else x^=a[i];
    return true;
}
同上。。。

5.查询第k小的值

inline ll query(ll k)
{
    ll res=0; int cnt=0;
    k-=flag;            //减去为0的情况
    if(!k) return 0;
    for(int i=0;i<=MAXN;++i)
    {
        for(int j=i-1;j>=0;--j)
            if(a[i]&(1ll<<j))   a[i]^=a[j];
        if(a[i])    tmp[cnt++]=a[i];
    }
    if(k>=(1ll<<cnt))   return -1;
    for(int i=0;i<cnt;++i)
        if(k&(1ll<<i)) res^=tmp[i];
    return res;
}
我们考虑进一步简化线性基。显然,一个线性基肯定可以表示为若干个形如2^i的数。
从高到低处理线性基每一位,对于每一位向后扫,如果当前数第i位为0,且线性基第i位不为0,则将当前数异或上a[i] 这一操作可以在O(n^2)的时间内解决。
经过这一步操作后,设线性基内共有cnt个数,则它们共可以表示出2^cnt个数。
随后,我们考虑将k二进制拆分,用与快速幂类似的方法就可以求出第k小值。

作者:Laleyendadelaeternid
链接:https://www.acwing.com/solution/content/3094/
来源:AcWing
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
#include <iostream>
#include <cstring>
#include <algorithm>

using namespace std;

typedef long long LL;
const int N = 10010;

LL a[N];

int main()
{
    int T;
    scanf("%d", &T);
    for (int C = 1; C <= T; C ++ )
    {
        printf("Case #%d:\n", C);
        int n;
        scanf("%d", &n);
        for (int i = 0; i < n; i ++ ) scanf("%lld", &a[i]);
        int k = 0;
        for (int i = 62; i >= 0; i -- )
        {
            for (int j = k; j < n; j ++ )
                if (a[j] >> i & 1)
                {
                    swap(a[j], a[k]);
                    break;
                }
            if (!(a[k] >> i & 1)) continue;
            for (int j = 0; j < n; j ++ )
                if (j != k && (a[j] >> i & 1))
                    a[j] ^= a[k];
            k ++ ;
            if (k == n) break;
        }
        reverse(a, a + k);

        int m;
        scanf("%d", &m);
        while (m -- )
        {
            LL x;
            scanf("%lld", &x);
            if (k < n) x -- ;
            if (x >= (1ll << k)) puts("-1");
            else
            {
                LL res = 0;
                for (int i = 0; i < k; i ++ )
                    if (x >> i & 1)
                        res ^= a[i];
                printf("%lld\n", res);
            }
        }
    }
    return 0;
}

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值