zoukankan      html  css  js  c++  java
  • P4619 [SDOI2018]旧试题

    P4619 [SDOI2018]旧试题

    题目描述

    求下面表达式的值

    [largesum_{i=1}^{A}sum_{j=1}^{B}sum_{k=1}^{C}sigma_{0}(ijk) mod(10^9+7) ]

    数据范围

    (1 le A,B,C le 100000)

    解题思路

    这题非常困难是因为推着推着式子变成了图论问题?

    让我们先来推一波式子把

    要用到的结论

    [large sigma_{0}(ijk)=sum_{x|i}sum_{y|j}sum_{z|k}[gcd(x,y)=1][gcd(y,z)=1][gcd(x,z)=1]\ [x=1] = sum_{d|x}mu(d) ]

    第一个式子的理解

    单独考虑一个质因子 P,i 中最大有 (P^a),j 中最大有 (P^b),k 中最大有 (P^c),那么 (ijk) 中有 (P^{a+b+c}), 现在我们尝试建立起映射,选因数时,如果 P 选了小于等于 a 个,那么全在 i 中选,显然满足全部互质的条件,大于 a 个但小于等于 a + b 个,那么优先在 i 中选,剩下的在 j 中选,然后将 i 清零,大于 a + b 也同理,也就是选质数时尽量靠前选,但我们只让最后一个被选到的有,前面的设为 0,这样可以保证互质,且是一种双射关系

    第二个式子的理解

    裸的莫比乌斯反演

    正式开始推式子

    [largesum_{i=1}^{A}sum_{j=1}^{B}sum_{k=1}^{C}sigma_{0}(ijk)\ large=sum_{i=1}^{A}sum_{j=1}^{B}sum_{k=1}^{C}sum_{x|i}sum_{y|j}sum_{z|k}[gcd(x,y)=1][gcd(y,z)=1][gcd(x,z)=1]\ large=sum_{x=1}^{A}sum_{y=1}^{B}sum_{z=1}^{C}[gcd(x,y)=1][gcd(y,z)=1][gcd(x,z)=1]sum_{i=1}^{frac Ax}sum_{j=1}^{frac By}sum_{k=1}^{frac Cz}\ large=sum_{x=1}^{A}sum_{y=1}^{B}sum_{z=1}^{C}[gcd(x,y)=1][gcd(y,z)=1][gcd(x,z)=1]lfloorfrac Ax floorlfloorfrac By floorlfloorfrac Cz floor\ large=sum_{x=1}^{A}sum_{y=1}^{B}sum_{z=1}^{C}sum_{u|x,u|y}mu(u)sum_{v|y,v|z}mu(v)sum_{w|x,w|z}mu(w)lfloorfrac Ax floorlfloorfrac By floorlfloorfrac Cz floor\ large=sum_{u=1}^{min(A,B)}sum_{v=1}^{min(B,C)}sum_{w=1}^{min(A,C)}mu(u)mu(v)mu(w)sum_{x=lcm(u,w)}lfloorfrac Ax floorsum_{y=lcm(u,v)}lfloorfrac By floorsum_{z=lcm(v,w)} lfloorfrac Cz floor\ large=sum_{u=1}^{min(A,B)}sum_{v=1}^{min(B,C)}sum_{w=1}^{min(A,C)}mu(u)mu(v)mu(w)F(lcm(u,w),A)F(lcm(u,v),B)F(lcm(v,w),C) ]

    然后就是超级神奇的操作

    我们把枚举的三元组抽象为一个三元环 (x, y, z),两个点间连边权为 lcm 的边,这个三元环对答案有贡献当且仅当

    • (mu(x) eq0 ~~mu(y) eq0~mu(z) eq0)
    • (lcm(x, y, z) le max(A, B, C))

    你觉得它可能会快,但你认为它还是暴力,事实上它跑的飞快(比纯暴力来说

    枚举三元环肯定不是暴力枚举,容易发现 lcm 的 (mu) 也不为零,我们可以直接枚举 lcm,将其质因数分解,根据质因数枚举其中一个端点,然后暴力枚举另一个端点,这样枚举的每一个都是合法的点对,时间复杂度 (Theta(m))

    打表发现有大概 70 多万条边,暴力枚举肯定不行,用上黑科技 三元环计数 可以做到 (Theta(msqrt m)),卡卡常勉强跑过。

    一发过了,但除法真的比乘法慢一大堆,请谨慎使用除法!!

    #pragma GCC optimize(3, "inline")
    #include <queue>
    #include <vector>
    #include <iostream>
    #include <cstdio>
    #include <cstring>
    #include <algorithm>
    #define MP make_pair
    #define ll long long
    #define fi first
    #define se second
    using namespace std;
    
    template <typename T>
    void read(T &x) {
        x = 0; bool f = 0;
        char c = getchar();
        for (;!isdigit(c);c=getchar()) if (c=='-') f=1;
        for (;isdigit(c);c=getchar()) x=x*10+(c^48);
        if (f) x=-x;
    }
    
    template<typename F>
    inline void write(F x)
    {
    	static short st[30];short tp=0;
    	if(x<0) putchar('-'),x=-x;
    	do st[++tp]=x%10,x/=10; while(x);
    	while(tp) putchar('0'|st[tp--]);
    	putchar('
    ');
    }
    
    template <typename T>
    inline void Mx(T &x, T y) { x < y && (x = y); }
    
    template <typename T>
    inline void Mn(T &x, T y) { x > y && (x = y); }
    
    #define re register
    const int Maxn = 300000, N = Maxn + 5;
    const int P = 1e9+7;
    ll f[N], mu[N], e[N], prime[N], p[N], tot;
    void prework(void) {
    	mu[1] = 1; f[1] = 1;
    	for (re int i = 2;i <= Maxn; i++) {
    		if (!e[i]) prime[++tot] = e[i] = i, mu[i] = -1, f[i] = p[i] = 2;
    		for (re int j = 1; prime[j] * i <= Maxn && j <= tot; j++) {
    			int t = prime[j] * i; e[t] = prime[j];
    			if (prime[j] == e[i]) { p[t] = p[i] + 1, f[t] = f[i] / p[i] * p[t]; break; } 
    			f[t] = f[i] * (p[t] = 2), mu[t] = mu[i] * -1;
    		}
    //		cout << f[i] << endl;
    		f[i] = f[i] + f[i-1]; if (f[i] >= P) f[i] -= P;
    	}
    }
    
    
    struct node {
    	int x, y, lcm;
    }ed[N*20];
    
    ll ans;
    ll vis[N], lm[N], a[N], b[N], c[N], A, B, C;
    ll st[N], deg[N], lim, tp, T;
    vector<pair<ll, ll> > v[N];
    inline void clear(void) {
    	lim = max(max(A, B), C), tot = ans = 0;
    	for (int i = 1;i <= lim; i++) 
    		a[i] = f[A/i], b[i] = f[B/i], c[i] = f[C/i], v[i].clear();
    }
    
    inline ll calc(int x, int y, int z) {
    	return a[x] * b[y] * c[z];
    }
    
    inline void build(void) {
    //	cout << calc(2, 1, 2) + calc(2, 2, 1) + calc(1, 2, 2) << endl;
    	memset(deg, 0, lim * 4 + 200);
    	for (int i = 1;i <= lim; i++) {
    		if (!mu[i]) continue; int x = i; tp = 0;
    //		if (i % 1000 == 0) cout << i << endl;
    		while (x != 1) st[++tp] = e[x], x /= e[x];
    		for (re int j = 0; j < (1 << tp); j++) {
    			int u = 1, t, v;
    			for (re int k = 1;k <= tp; k++)
    				if (j & (1 << (k - 1))) u *= st[k];
    			t = i / u;
    			for (int s = j; ; s = (s - 1) & j) {
    				v = t;
    				for (int p = 0;p < tp; p++)
    					if (s >> p & 1) v *= st[p + 1];
    //				cout << u << ' ' << v << endl;
    				if (u > v) {
    					ed[++tot] = (node) {u, v, i}; deg[u]++, deg[v]++;
    //					cout << u << ' ' << i << endl;
    //					cout << calc(u, i, i) + calc(i, u, i) + calc(i, i, u) << endl;
    					ans += mu[v] * (calc(u, i, i) + calc(i, u, i) + calc(i, i, u));
    					ans += mu[u] * (calc(v, i, i) + calc(i, v, i) + calc(i, i, v));
    				}
    				if (!s) break;
    			}
    		}
    		ans += mu[i] * calc(i, i, i);
    	}
    	for (int i = 1;i <= tot; i++) {
    		int x = ed[i].x, y = ed[i].y;
    		if (deg[x] > deg[y] || (deg[x] == deg[y] && x > y)) swap(x, y);
    		v[x].emplace_back(y, ed[i].lcm);
    	}
    }
    
    void work(void) {
    	memset(vis, 0, lim * 4 + 200);
    //	cout << calc(10, 5, 2) + calc(10, 2, 5) + calc(2, 5, 10) + calc(2, 10, 5) << endl;
    	for (int i = 1;i <= lim; i++) {
    		for (pair<int, int> to: v[i]) 
    			vis[to.fi] = i, lm[to.fi] = to.se;
    		for (pair<int, int> to: v[i]) {
    			for (pair<int, int> To: v[to.fi]) {
    				if (vis[To.fi] != i) continue;
    //				int pre = ans;
    //				cout << lm[To.fi] << ' ' << to.se << ' ' << To.se << endl;
    				ans += mu[i] * mu[to.fi] * mu[To.fi] * 
    					( calc(lm[To.fi], to.se, To.se) 
    					+ calc(lm[To.fi], To.se, to.se)
    					+ calc(to.se, lm[To.fi], To.se) 
    					+ calc(to.se, To.se, lm[To.fi])
    					+ calc(To.se, lm[To.fi], to.se)
    					+ calc(To.se, to.se, lm[To.fi]) );
    //				cout << ans - pre << endl;
    			}
    		}
    		ans %= P;
    	}
    	ans = (ans % P + P) % P;
    }
    
    int main() {
    	for (prework(), read(T); T; T--) {
    		read(A), read(B), read(C);
    		clear(), build(), work(), write(ans);
    	}
    	return 0;
    }
    /*
    
    1
    100000 100000 100000
    
    */
    
  • 相关阅读:
    EF异常:对一个或多个实体的验证失败
    申请一个带表情的个性网站
    第01周学习提升:测试理论及功能测试------测试理论
    学习方向:先学习自动化。
    从0开始学习性能测试
    【CI/CD】使用Jenkins部署VUE项目到windows服务器_配置Jenkins通过SSH连接windows server机器(3)
    【CI/CD】使用Jenkins部署VUE项目到windows服务器_配置Jenkins拉取git代码(2)
    【CI/CD】使用Jenkins部署VUE项目到windows服务器_任务调研+Jenkins安装(1)
    【CI/CD】使用Jenkins部署VUE项目到windows服务器_配置Jenkins项目(4)
    【CI/CD】Jenkins部署项目后发送邮件配置
  • 原文地址:https://www.cnblogs.com/Hs-black/p/13086618.html
Copyright © 2011-2022 走看看