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}^b\sum_{j=c}^d[gcd(i,j)\in[l,r]]lcm(i,j) \]

搞个前缀和

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

对于每一个

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

可以容斥一下

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

枚举gcd

\[\sum_{d=1}^p\sum_{i=1}^n\sum_{j=1}^m[gcd(i,j)==d]\frac{i*j}{d} \]

把d除上去

\[\sum_{d=1}^p\sum_{i=1}^{\lfloor\frac n d \rfloor}\sum_{j=1}^{\lfloor\frac m d \rfloor}[gcd(i,j)==1]i * j * d \]

把d提到前面

\[\sum_{d=1}^pd\sum_{i=1}^{\lfloor\frac n d \rfloor}\sum_{j=1}^{\lfloor\frac m d \rfloor}[gcd(i,j)==1]i * j \]

利用卷积代换

\[\sum_{d=1}^pd\sum_{i=1}^{\lfloor\frac n d \rfloor}\sum_{j=1}^{\lfloor\frac m d \rfloor}\sum_{k|gcd(i,j)}\mu(k)*i * j \]

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

\[\sum_{d=1}^pd\sum_{k=1}^{min(\lfloor\frac n d \rfloor,\lfloor\frac m d \rfloor)}\mu(k)*k*k\sum_{i=1}^{\lfloor\frac{n}{kd} \rfloor}i\sum_{j=1}^{\lfloor\frac {m}{kd} \rfloor}j \]

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

于是,我们进行kd换T

枚举T

\[\sum_{T=1}^{min(n,m)}T\sum_{d|T}^{p}\mu(\frac T d)*\frac T d\sum_{i=1}^{\lfloor\frac{n}{T} \rfloor}i\sum_{j=1}^{\lfloor\frac {m}{T} \rfloor}j \]

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

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

然后直接整数分块\(O(m\sqrt 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\n", ans[i]);
	return 0;
}
posted @ 2019-03-07 17:19  olinr  阅读(193)  评论(0编辑  收藏  举报