[Sdoi2014]数表

题意

<body> <center><h2>3529: [Sdoi2014]数表</h2><span class="green">Time Limit: </span>10 Sec&nbsp;&nbsp;<span class="green">Memory Limit: </span>512 MB<br><span class="green">Submit: </span>3009&nbsp;&nbsp;<span class="green">Solved: </span>1559<br>[<a href="submitpage.php?id=3529">Submit</a>][<a href="problemstatus.php?id=3529">Status</a>][<a href="bbs.php?id=3529">Discuss</a>]</center><h2>Description</h2><div class="content"><div>有一张 n×m 的数表,其第 i 行第 j 列(1 &lt;= i &lt;= n, 1 &lt;= j &lt;= m)的数值为</div> <div>能同时整除 i 和 j 的所有自然数之和。给定 a , 计算数表中不大于 a 的数之和。</div></div><h2>Input</h2><div class="content"><div>输入包含多组数据。</div> <div>输入的第一行一个整数Q表示测试点内的数据组数</div> <div>接下来Q行,每行三个整数n,m,a(|a| &lt; =10^9)描述一组数据。</div> <div>1 &lt; =N.m &lt; =10^5 &nbsp;, 1 &lt; =Q &lt; =2×10^4</div></div><h2>Output</h2><div class="content"><p>对每组数据,输出一行一个整数,表示答案模2^31的值。</p></div><h2>Sample Input</h2> <div class="content"><span class="sampledata">2<br> 4 4 3<br> 10 10 5<br> </span></div><h2>Sample Output</h2> <div class="content"><span class="sampledata">20<br> 148</span></div><h2>HINT</h2> <div class="content"><p></p></div><h2>Source</h2> <div class="content"><p><a href="problemset.php?search=Round 1 Day 1">Round 1 Day 1</a></p></div><center>[<a href="submitpage.php?id=3529">Submit</a>][<a href="problemstatus.php?id=3529">Status</a>][<a href="bbs.php?id=3529">Discuss</a>]</center><br> <a href="./"><span class="red">HOME</span></a> <a href="javascript:history.go(-1)"><span class="red">Back</span></a>
</body>

分析

参照MashiroSky的题解。

题意

多组询问,每次给出\({n,m,a}\)。求$${\sum_{i=1}n\sum_{j=1}m[\sum_{d|i,d|j}d<=a]\sum_{d|i,d|j}d}$$

Solution

\({a}\)的限制很不好做,我们不妨先来看看如果没有\({a}\)的限制,是个什么情况。

\[\begin{aligned} & \sum_{i=1}^n\sum_{j=1}^m\sum_{d|i,d|j}d \\ =&\sum_{i=1}^n\sum_{j=1}^m\sum_{d=1}^{min(n,m)}f(d)[gcd(i,j)=d] \end{aligned} \]

这里\({f(d)}\)表示\({d}\)的约数和,由约数和定理:$${d=p_1{a_1}p_2p_3{a_3}······p_k{a_k}}$$

\[{f(d)=(p_1^0+p_1^1+······+p_1^{a_1})(p_2^0+p_2^1+······+p_2^{a_2})······(p_k^0+p_k^1+······+p_k^{a_k})} \]

我们发现当\({p,q}\)互质时,\({f(pq)=f(p)f(q)}\)\({f}\)是个积性函数。也就是说\({f}\)可以线性筛求解,具体求法可以看代码。回到上面的问题,我们继续变化这个式子。

\[\begin{aligned} &\sum_{i=1}^n\sum_{j=1}^m\sum_{d=1}^{min(n,m)}f(d)[gcd(i,j)=d] \\ =&\sum_{d=1}^{min(n,m)}f(d)\sum_{i=1}^{\lfloor{n/d}\rfloor}\sum_{j=1}^{\lfloor{m/d}\rfloor}[gcd(i,j)=1] \\ =&\sum_{d=1}^{min(n,m)}f(d)\sum_{t=1}^{min(\lfloor{n/d}\rfloor,\lfloor{m/d}\rfloor)}μ(t)\lfloor\frac{n}{dt}\rfloor\lfloor\frac{m}{dt}\rfloor \end{aligned} \]

我们令\({Q=dt}\),先枚举\({Q}\),得到:$${\sum_{Q=1}^{min(n,m)}\lfloor\frac{n}{Q}\rfloor\lfloor\frac{m}{Q}\rfloor\sum_{d|Q}f(d)μ(\frac{Q}{d})}$$

我们对\({f}\)\({μ}\)函数的狄利克雷卷积\({g(Q)=\sum_{d|Q}f(d)μ(\frac{Q}{d})}\)预处理前缀和,前面的\({\lfloor\frac{n}{Q}\rfloor\lfloor\frac{m}{Q}\rfloor}\)分块,那么这个式子的计算就达到了\({\sqrt{n}}\)

考虑如何加入限制\({a}\)

我们可以离线,将询问按照\({a}\)从小到大排序,\({f}\)函数按照从小到大排序。询问的时候,我们就动态维护一个树状数组\({c}\)。树状数组的每一位\({c[x]}\)存的是卷积\({g(x)}\)\({f(d)}\)不超过\({a}\)的数之和。数据组数为\({T}\),询问复杂度\({O(T\sqrt{n}logn)}\)

细节

这题复杂度有点卡,对于取模运算我们可以对\({int}\)自然溢,最后再\({\&2147483647}\)就可以了。

代码

#include<bits/stdc++.h>
#define rg register
#define il inline
#define co const
template<class T>il T read(){
    rg T data=0,w=1;
    rg char ch=getchar();
    while(!isdigit(ch)){
        if(ch=='-') w=-1;
        ch=getchar();
    }
    while(isdigit(ch))
        data=data*10+ch-'0',ch=getchar();
    return data*w;
}
template<class T>il T read(rg T&x){
    return x=read<T>();
}
typedef long long ll;

co int maxn=1e5+1,INF=0x7fffffff;
int mu[maxn],ans[maxn],c[maxn],p[maxn],t[maxn],g[maxn];
struct F{int d,num;}f[maxn];
struct Q{int n,m,a,id;}q[maxn];
bool cmpT(co Q&a,co Q&b) {return a.a<b.a;}
bool cmpt(co F&a,co F&b) {return a.d<b.d;}
#define lowbit(x) (x&-x)
int power(int a,int b){
	int res=1;
	while(b){
		if(b&1) res*=a;
		a*=a,b>>=1;
	}
	return res;
}
void add(int x,int val){
	for(int i=x;i<maxn;i+=lowbit(i)) c[i]+=val;
}
int query(int x){
	int s=0;
	for(int i=x;i>=1;i-=lowbit(i)) s+=c[i];
	return s;
}
int main(){
//	freopen(".in","r",stdin),freopen(".out","w",stdout);
	mu[1]=1,f[1].d=f[1].num=1;
	for(int i=2;i<maxn;++i){
		f[i].num=i;
		if(!p[i]) mu[i]=-1,f[i].d=t[i]=1+i,g[i]=1,p[++p[0]]=i;
		for(int j=1;j<=p[0]&&i*p[j]<maxn;++j){
			p[i*p[j]]=1;
			if(i%p[j]==0){
				mu[i*p[j]]=0;
				g[i*p[j]]=g[i]+1;
				t[i*p[j]]=t[i]+power(p[j],g[i]+1);
				f[i*p[j]].d=f[i].d/t[i]*t[i*p[j]];
			}
			else{
				mu[i*p[j]]=-mu[i];
				f[i*p[j]].d=f[i].d*f[p[j]].d;
				g[i*p[j]]=1;t[i*p[j]]=p[j]+1;
			}
		}
	}
	int T=read<int>();
	for(int i=1;i<=T;++i) read(q[i].n),read(q[i].m),read(q[i].a),q[i].id=i;
	std::sort(q+1,q+T+1,cmpT);
	std::sort(f+1,f+maxn,cmpt);
	for(int now=0,i=1;i<=T;++i){
		while(now+1<maxn&&f[now+1].d<=q[i].a){
			++now;
			for(int j=1;j*f[now].num<maxn;++j)
				add(j*f[now].num,mu[j]*f[now].d);
		}
		int n=q[i].n,m=q[i].m;
		if(n>m) std::swap(n,m);
		for(int j=1,k;j<=n;j=k+1){
			k=std::min(n/(n/j),m/(m/j));
			ans[q[i].id]+=(n/j)*(m/j)*(query(k)-query(j-1));
		}
		ans[q[i].id]&=INF;
	}
	for(int i=1;i<=T;++i) printf("%d\n",ans[i]);
	return 0;
}

posted on 2019-03-06 18:02  autoint  阅读(109)  评论(0编辑  收藏  举报

导航