$ 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}{题解})
题目就是要求
搞个前缀和
对于每一个
可以容斥一下
枚举gcd
把d除上去
把d提到前面
利用卷积代换
枚举约数,形成常见的形式
然而面对多组数据,还有1e5的数据范围,这肯定是不行的
于是,我们进行kd换T
枚举T
要注意,中间的不能预处理!! 有限制!!
考虑把询问拆开,存成两个询问,按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;
}