[SDOI2014]数表

题目链接:[SDOI2014]数表

题意:

T 组数据,每次给出 n,m,a ,在 nm 矩阵的每个位置上有一个数值,第 i 行第 j 列数值为能同时整除 ij 的所有自然数之和,求 nm 矩阵中所有不超过 a 的数值和。(T2104;1n,m105)


首先不考虑小于等于 a 的限制。

能同时整除 ij 就是能整除 gcd(i,j),所有每个位置上的数即为 gcd 的因子和 σ(gcd(i,j))

f(x)表示 xn,m 数表中出现次数。

f(x)=i=1nj=1m[gcd(i,j)=x]

老朋友了。。。

F(x)=x|df(d)=nxmx

f(x)=x|dμ(dx)F(d)=x|dμ(dx)nxmx

答案为每一个 x 约数和乘以出现次数。

Ans=i=1nσ(i)f(i)

=i=1nσ(i)i|dμ(di)ndmd

将枚举 i 的倍数改为枚举 d 的因数。

Ans=d=1nndmdi|dσ(i)μ(di)

前面可以用整除分块,后面做前缀和。

g(x)=i|xσ(i)μ(xi)

g 做个前缀和,分块的时候乘上前面的 ndmd


然后考虑有 a 的限制,则对于 g(x) 来讲当 x 的因子 i 满足σ(i)a 时其对 g 没有贡献。

将询问离线按 a 升序排列,每当 a 增加时就将 σ(i)=a 的贡献加上,可以枚举倍数来更新 g 的值,由于 g 值在变化且需要快速求出前缀和,值需要用树状数组动态维护。

瓶颈复杂度为O(Tnlog(n))。竟然可过?真神奇~

#include <algorithm> #include <iostream> #include <cstring> #include <cstdio> #include <cmath> #define QWQ cout<<"QwQ"<<endl; #define uint unsigned int #include <vector> #include <queue> #include <stack> #include <map> using namespace std; const uint N=101010; const uint qwq=303030; const uint inf=0x3f3f3f3f; uint T; uint n,m; uint big; uint vis[N],p[N],cnt,mu[N]; uint f[N],g[N]; uint tree[N]; uint ans[N]; struct E{ uint zhi,id; } s[N]; inline bool cmpe(E aa,E bb) { return aa.zhi < bb.zhi; } struct D{ uint x,y,a,id; } q[N]; inline bool cmpd(D aa,D bb) { return aa.a < bb.a; } inline uint read() { uint sum1 = 0, ff = 1; char c = getchar(); while(c<'0' || c>'9') { if(c=='-') ff = -1; c = getchar(); } while(c>='0'&&c<='9') { sum1 = sum1 * 10 + c - '0'; c = getchar(); } return sum1 * ff; } void qiu(uint h) { mu[1] = f[1] = 1; for(uint i=2;i<=h;i++) { if(!vis[i]) { p[++cnt] = i; mu[i] = -1; f[i] = i+1; g[i] = 1; } for(uint j=1; j<=cnt && i*p[j]<=h; j++) { uint now = i*p[j]; vis[now] = 1; if(i%p[j]==0) { f[now] = f[i] * p[j] + g[i]; g[now] = g[i]; break; } f[now] = f[i] * (p[j]+1); g[now] = f[i]; mu[now] = -mu[i]; } } for(uint i=1;i<=h;i++) s[i].id = i, s[i].zhi = f[i]; sort(s+1,s+h+1,cmpe); } inline void add(uint we,uint z) { for(uint i=we;i<=big;i+=(i&-i)) tree[i] += z; } inline uint ask(uint we) { uint res = 0; for(uint i=we;i;i-=(i&-i)) res += tree[i]; return res; } inline void insert(uint d) { for(uint i=1;i*d<=big;i++) add(i*d,f[d]*mu[i]); } int main() { qiu(big=N-1000); T = read(); for(uint i=1;i<=T;i++) q[i].x = read(), q[i].y = read(), q[i].a = read(), q[i].id = i; sort(q+1,q+T+1,cmpd); uint nowa = 0, nows = 1; for(uint i=1;i<=T;i++) { while(nowa < q[i].a) { nowa++; while(s[nows].zhi <= nowa) insert(s[nows++].id); } n = q[i].x; m = q[i].y; if(n>m) swap(n,m); uint last = 0, now = 0; for(uint l=1,r;l<=n;l=r+1) { r = min( n/(n/l), m/(m/l) ); now = ask(r); ans[ q[i].id ] += (n/l) * (m/l) * (now-last); last = now; } } uint tot = (1<<31); for(uint i=1;i<=T;i++) cout<<ans[i]%tot<<"\n"; return 0; }

细节还蛮多的,但是一A了,有被爽到~。。。


彩蛋:

由于此题答案要对231取模:

159巨佬TQL!orz


__EOF__

本文作者枫叶晴
本文链接https://www.cnblogs.com/maple276/p/12746562.html
关于博主:菜菜菜
版权声明:呃呃呃
声援博主:呐呐呐
posted @   maple276  阅读(130)  评论(1编辑  收藏  举报
编辑推荐:
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· 开发者必知的日志记录最佳实践
阅读排行:
· winform 绘制太阳,地球,月球 运作规律
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· AI 智能体引爆开源社区「GitHub 热点速览」
· Manus的开源复刻OpenManus初探
· 写一个简单的SQL生成工具
点击右上角即可分享
微信分享提示