• 2019.3.7考试T2 离线数论??


    $ color{#0066ff}{ 题目描述 }$

    一天,olinr 在 luogu.org 刷题,一点提交,等了一分钟之后,又蛙又替。
    olinr 发动了他的绝招,说:“为啥啊???”此时 leigehhh 拿着 6 个 map 走了过来,说:
    “你这个维护一个破(pre)就行了啊” olinr 恍然大悟,问 GMPotlc,“琛哥你还有 D 吗我要
    维护一个 D”。
    olinr 从 GMPotlc 那里得到了一块 n*m 大小的 D,用来种植 xkj。
    由于光照、二氧化碳浓度、温度等原因,olinr 得到的 D 有一个特性,对于第 i 行第 j
    列种植的 xkj,一共有 lcm(i,j)个,并且营养程度为 gcd(i,j)。
    olinr 希望将他的 D 的部分 xkj 出售给肯德基三人篮球赛举办方 KKF 用以在 NKFCP(全
    国青少年肯德基三人篮球赛联赛)、NKFCWC(全国青少年肯德基三人篮球赛冬令营)、CKFC
    (肯德基三人篮球赛中国国家队选拔赛)、IKFC(肯德基三人篮球赛全球赛)、NKFC(全国
    青少年肯德基三人篮球赛)、APKFC(亚洲太平洋地区肯德基三人篮球赛)等赛事上提供服
    务,KKF 为了保证 KFCers 的身心健康,只接受营养程度在[a,b]之间的 xkj,请你对于 olinr
    的每次询问输出他能够出售的 xkj 数量。

    (color{#0066ff}{输入格式})

    第一行输入一个整数 q 代表 olinr 的询问数量
    接下来 q 行每行 6 个整数 x1 x2 y1 y2 a b 表示 olinr 希望将横坐标在[x1, x2]范围并且纵
    坐标在[y1, y2]范围并且营养程度在[a, b]范围的 xkj 出售。每次询问之间相互独立。

    (color{#0066ff}{输出格式})

    输出 q 行,每行 1 个数表示答案。由于出题人故意卡你所以请输出 mod998244353 结果

    (color{#0066ff}{输入样例})

    5
    1 5 1 6 2 8
    3 8 4 9 2 20
    1 4 1 4 1 4
    2 5 7 9 3 10
    9 9 3 3 3 3
    

    (color{#0066ff}{输出样例})

    46
    151
    72
    17
    9
    

    (color{#0066ff}{数据范围与提示})

    对于 40%的数据,n,m,q<=1000;
    对于 100%的数据,n,m<=(10^5) (q<=10^4)
    我们保证每次询问数据 1<=x1<=x2<=n,1<=y1<=y2<=m,并且 1<=a<=b<=(10^5)
    提示:n 和 m 不会在输入中出现,但是保证满足数据范围

    (color{#0066ff}{题解})

    题目就是要求

    [sum_{i=a}^bsum_{j=c}^d[gcd(i,j)in[l,r]]lcm(i,j) ]

    搞个前缀和

    [sum_{i=a}^bsum_{j=c}^d[gcd(i,j)le r]lcm(i,j) -sum_{i=a}^bsum_{j=c}^d[gcd(i,j)le l-1]lcm(i,j) ]

    对于每一个

    [sum_{i=a}^bsum_{j=c}^d[gcd(i,j)le d]lcm(i,j) ]

    可以容斥一下

    [sum_{i=1}^nsum_{j=1}^m[gcd(i,j)le p]frac{i*j}{gcd(i,j)} ]

    枚举gcd

    [sum_{d=1}^psum_{i=1}^nsum_{j=1}^m[gcd(i,j)==d]frac{i*j}{d} ]

    把d除上去

    [sum_{d=1}^psum_{i=1}^{lfloorfrac n d floor}sum_{j=1}^{lfloorfrac m d floor}[gcd(i,j)==1]i * j * d ]

    把d提到前面

    [sum_{d=1}^pdsum_{i=1}^{lfloorfrac n d floor}sum_{j=1}^{lfloorfrac m d floor}[gcd(i,j)==1]i * j ]

    利用卷积代换

    [sum_{d=1}^pdsum_{i=1}^{lfloorfrac n d floor}sum_{j=1}^{lfloorfrac m d floor}sum_{k|gcd(i,j)}mu(k)*i * j ]

    枚举约数,形成常见的形式

    [sum_{d=1}^pdsum_{k=1}^{min(lfloorfrac n d floor,lfloorfrac m d floor)}mu(k)*k*ksum_{i=1}^{lfloorfrac{n}{kd} floor}isum_{j=1}^{lfloorfrac {m}{kd} floor}j ]

    然而面对多组数据,还有1e5的数据范围,这肯定是不行的

    于是,我们进行kd换T

    枚举T

    [sum_{T=1}^{min(n,m)}Tsum_{d|T}^{p}mu(frac T d)*frac T dsum_{i=1}^{lfloorfrac{n}{T} floor}isum_{j=1}^{lfloorfrac {m}{T} floor}j ]

    要注意,中间的不能预处理!! 有限制!!

    考虑把询问拆开,存成两个询问,按p从小到大排序,维护一个d的单调指针,每次把(le p)的k加进去(枚举倍数,用树状数组记录前缀和

    然后直接整数分块(O(msqrt nlogn))

    #include<bits/stdc++.h>
    #define LL long long
    LL in() {
    	char ch; LL x = 0, f = 1;
    	while(!isdigit(ch = getchar()))(ch == '-') && (f = -f);
    	for(x = ch ^ 48; isdigit(ch = getchar()); x = (x << 1) + (x << 3) + (ch ^ 48));
    	return x * f;
    }
    const int mod = 998244353;
    const int maxn = 1e5 + 100;
    int pri[maxn], tot;
    LL mu[maxn];
    bool vis[maxn];
    struct node {
    	LL x, xx, y, yy, lim, opt, id;
    	friend bool operator < (const node &a, const node &b) { return a.lim < b.lim; }
    	node(LL x = 0, LL xx = 0, LL y = 0, LL yy = 0, LL lim = 0, LL opt = 0, LL id = 0): x(x), xx(xx), y(y), yy(yy), lim(lim), opt(opt), id(id) {}
    }e[maxn << 1];
    struct Tree {
    protected:
    	LL st[maxn];
    	int low(int x) { return x & (-x); }
    public:
    	void add(int pos, LL k) { while(pos < maxn) (st[pos] += k) %= mod, pos += low(pos); }
    	LL query(int pos) { LL re = 0; while(pos) (re += st[pos]) %= mod, pos -= low(pos); return re; }
    }s;
    LL getsum(LL len) {
    	return ((len * (len + 1)) >> 1) % mod;
    }
    void predoit() {
    	mu[1] = 1;
    	for(int i = 2; i < maxn; i++) {
    		if(!vis[i]) pri[++tot] = i, mu[i] = -1;
    		for(int j = 1; j <= tot && i * pri[j] < maxn; j++) {
    			vis[i * pri[j]] = true;
    			if(i % pri[j] == 0) break;
    			else mu[i * pri[j]] = -mu[i];
    		}
    	}
    }
    LL work(LL n, LL m) {
    	LL ans = 0;
    	for(LL l = 1, r; l <= std::min(n, m); l = r + 1) {
    		r = std::min(n / (n / l), m / (m / l));
    		(ans += getsum(n / l) * getsum(m / l) % mod * (((s.query(r) - s.query(l - 1)) % mod) + mod) % mod) %= mod;
    	}
    	return ans;
    }
    
    
    LL getans(LL x, LL y, LL xx, LL yy) { 
    	return ((work(xx, yy) - work(x - 1, yy) - work(xx, y - 1) + work(x - 1, y - 1)) % mod + mod) % mod;
    }
    int main() {
    	freopen("plot.in", "r", stdin);
    	freopen("plot.out", "w", stdout);
    	predoit();
    	int num = 0, T = in();
    	for(int i = 1; i <= T; i++) {
    		LL x = in(), xx = in(), y = in(), yy = in(), a = in(), b = in();
    		e[++num] = node(x, xx, y, yy, a - 1, -1, i);
    		e[++num] = node(x, xx, y, yy, b, 1, i);
    	}
    	static LL ans[maxn];
    	std::sort(e + 1, e + num + 1);
    	LL now = 1;
    	for(int i = 1; i <= num; i++) {
    		while(now <= e[i].lim) {
    			for(LL i = now; i < maxn; i += now) s.add(i, (((i * mu[i / now] % mod * (i / now) % mod) + mod) % mod));
    			now++;
    		}
    		(ans[e[i].id] += ((e[i].opt * getans(e[i].x, e[i].y, e[i].xx, e[i].yy) % mod) + mod) % mod) %= mod;
    	}
    	for(int i = 1; i <= T; i++) printf("%lld
    ", ans[i]);
    	return 0;
    }
    
  • 相关阅读:
    VisualSVN 服务不能自动启动的问题
    "There is already an open DataReader associated with this Command which must be closed first"错误
    LINQ 中调用存储过程自动绑定列名
    vs2008中调试iis7.0托管的程序
    SQL SERVER 2005的一个怪问题: 在查询结果面板中编辑失败.
    在VS2003下把一个DataTable Update 到数据库
    帮人解决一个小问题:QQ空间登录时脚本错误造成无法登入
    关于Sys未定义错误
    多层母版页嵌套中, 内层母版页的事件默认不触发
    线程同步
  • 原文地址:https://www.cnblogs.com/olinr/p/10490951.html
Copyright © 2020-2023  润新知