首页 > 其他分享 >数表

数表

时间:2024-05-05 21:33:07浏览次数:11  
标签:prime frac min int sum 数表 ki

这是一道莫比乌斯反演的题目

我们首先直接根据题目列式子,对位置\((i,j)\),其在数表上的值为$$\sum_{n|i且n|j}n$$,很显然就是$$\sum_{n|gcd(i,j)}n$$

我们先不考虑\(a\)的限制,题目的答案就是

\[\sum_{i=1}^{n}\sum_{j=1}^{m}\sum_{n|gcd(i,j)}n \]

,设\(F(i)\)表示\(i\)的约数和,答案就可以写成

\[\sum_{i=1}^{n}\sum_{j=1}^{m}F(gcd(i,j)) \]

我们考虑\(gcd(i,j)=k\)的有多少对,这个时候就可以想起“破译密码”这道题目,也就是莫比乌斯反演,答案就可以写成(除法都是整除)

\[\sum_{k=1}^{min(n,m)}F(k)\sum_{i=1}^{min(\frac{n}{k},\frac{m}{k})}u(i)\frac{\frac{n}{k}}{i}\frac{\frac{m}{k}}{i}=\sum_{k=1}^{min(n,m)}F(k)\sum_{i=1}^{min(\frac{n}{k},\frac{m}{k})}u(i)\frac{n}{ki}\frac{m}{ki} \]

现在考虑\(a\)的限制,我们发现当\(F(k)>a\)时,贡献为\(0\),此时为了快速统计,可以离线,将询问按照\(a\)降序排序,然后每次将比当前\(a\)大的贡献清零,然后再数论分块。这里用树状数组就好了

但是这样仍然会超时,此时我们没有别的办法化简了,除了换序(所以以后没有别的办法的时候考虑换序)

如果不换序的话,是分块套分块,复杂度退化为\(O(n)\);为了只分块一次,我们要将\(\frac{n}{ki}\frac{m}{ki}\)提到外面,所以我们令\(ki=T\),将式子重写为

\[\sum_{k=1}^{min(n,m)}F(k)\sum_{k|T}u(\frac{T}{k})\frac{n}{T}\frac{m}{T} \]

这个时候要换序的话,跟微积分一样,先把所有函数写在一起,有

\[\sum_{k=1}^{min(n,m)}\sum_{k|T}F(k)u(\frac{T}{k})\frac{n}{T}\frac{m}{T}=\sum_{T=1}^{min(n,m)}\sum_{k|T}F(k)u(\frac{T}{k})\frac{n}{T}\frac{m}{T} \]

然后再把该提出的提出,有

\[\sum_{T=1}^{min(n,m)}\frac{n}{T}\frac{m}{T}\sum_{k|T}F(k)u(\frac{T}{k}) \]

令\(f(T)=\sum_{k|T}F(k)u(\frac{T}{k})\)

原式可以写成

\[\sum_{T=1}^{min(n,m)}\frac{n}{T}\frac{m}{T}f(T) \]

预处理出\(f\)的前缀和就可以数论分块了

预处理\(f\)的话,可以用类似倍除法的思想,在\(O(nlogn)\)的时间内完成(也可以用线性筛,但是很麻烦,没必要);然后注意仍然要倒序处理询问,而且每次处理的时候不是将某个\(f\)直接清空,而是清空某个\(f\)的一部分(因为更大的数的约数和不一定更大,而只有\(F>a\)了才会没有贡献);然后还有注意写除法时一定要打括号。具体见以下代码

#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int N=1e5+10,Q=2e4+10;
const ll mod=1ll<<31;
struct node
{
	int n,m,a,id;
}qry[Q];
int u[N],v[N],prime[N],cnt;
ll c[N],f[N],F[N],ans[N];
vector<int> pos[N*5];
void init(int n)
{
	u[1]=1;
	for(int i=2;i<=n;i++)
	{
		if(!v[i])
		{
			v[i]=i;
			prime[++cnt]=i;
			u[i]=-1;
		}
		for(int j=1;j<=cnt;j++)
		{
			if(prime[j]>v[i]||prime[j]>n/i) break;
			v[i*prime[j]]=prime[j];
			if(i%prime[j]==0||!u[i]) u[i*prime[j]]=0;
			else u[i*prime[j]]=-u[i];
		}
	}
}
bool cmp(node i,node j)
{
	return i.a>j.a;
}
void add(int x,int d)
{
	for(;x<=N-10;x+=x&-x) c[x]=(c[x]+d+mod)%mod;
}
ll ask(int x)
{
	ll res=0;
	for(;x;x-=x&-x) res=(res+c[x])%mod;
	return res;
}
int main()
{
	init(N-10);
	for(int d=1;d<=N-10;d++)
	for(int j=d;j<=N-10;j+=d)
	F[j]+=d;
	for(int d=1;d<=N-10;d++)
	for(int j=d;j<=N-10;j+=d)
	f[j]=(F[d]*u[j/d]+f[j])%mod;//倍除法 
	int Max=0;
	for(int i=1;i<=N-10;i++) 
	{
		pos[F[i]].push_back(i);
		Max=max(Max,(int)F[i]);
	}
	int q;
	scanf("%d",&q);
	for(int i=1;i<=q;i++)
	{
		scanf("%d%d%d",&qry[i].n,&qry[i].m,&qry[i].a);
		qry[i].id=i;
	}
	sort(qry+1,qry+q+1,cmp);
	for(int i=1;i<=N-10;i++)
	add(i,f[i]);
	for(int i=1;i<=q;i++)
	{
		while(Max>qry[i].a)
		{
			for(int j=0;j<pos[Max].size();j++) 
			for(int k=pos[Max][j];k<=N-10;k+=pos[Max][j]) 
			add(k,-F[pos[Max][j]]*u[k/pos[Max][j]]);
			//必须要这么清零,不能直接写成
			/*
			for(int j=0;j<pos[Max].size();j++) 
			add(pos[Max][j],-f[pos[Max][j]]);
			*/ 
			Max--;
		}
		for(int l=1,r;l<=min(qry[i].n,qry[i].m);l=r+1)
		{
			r=min(min(qry[i].n,qry[i].m),min(qry[i].n/(qry[i].n/l),qry[i].m/(qry[i].m/l)));
			ans[qry[i].id]=(ans[qry[i].id]+(ask(r)-ask(l-1)+mod)%mod*(qry[i].n/l)%mod*(qry[i].m/l)%mod)%mod;//注意后面两个(qry[i].n/l)和 (qry[i].m/l)一定要打括号,这样才能整除 
		}
	}
	for(int i=1;i<=q;i++) printf("%lld\n",ans[i]);
	return 0;
} 

标签:prime,frac,min,int,sum,数表,ki
From: https://www.cnblogs.com/dingxingdi/p/18173921

相关文章

  • 数表 题解
    “当你想不出来一道题的时候就想一下排序”先不考虑\(a\),求\[\begin{aligned}&\sum_{i=1}^n\sum_{j=1}^m\sigma_1(\gcd(i,j))\\=&\sum_{i=1}^n\sum_{j=1}^m\sum_{d=1}^{\min(n,m)}[d=\gcd(i,j)]\sigma_1(d)\\=&\sum_{d=1}^{\min(n,m)}\sigma_1(d)\sum_......
  • 数据库的关系代数表达式
    数据库的关系代数表达式关系代数是一种用于描述和操作关系数据库的形式化语言。它提供了一组基本操作,包括选择、投影、并、差、笛卡尔积等,可以用来表示和操作关系数据库中的数据。基本操作选择(Selection):从关系中选择满足指定条件的元组。投影(Projection):从关系中选择指定的属......
  • vptr和vtbl(虚指针和虚函数表)
    vptr和vtbl(虚指针和虚函数表)c++代码的抽象类是->类当中只包含纯虚函数当一个类有虚函数,即便类当中没有成员变量.他的对象大小也会有一根指针大小->由操作系统决定指针多大虚函数子类的对象里面有父类的成分示例结构代码:#pragma#ifndef__VPTR_AND_VTBL__#define__V......
  • 虚函数以及虚函数表
    虚函数的作用主要是实现了多态的机制。简单来说,就是用父类型别的指针指向其子类的实例,然后通过父类的指针调用实际子类的成员函数。这样子就可以让父类的指针有“多种形态”,这是一种泛型技术。就是试图使用不变的代码来实现可变的算法。每个对象占用存储空间的只是该对象的数据部......
  • C++中的虚函数和虚函数表
    在上面一篇博客中 https://www.cnblogs.com/wphl-27/p/18111083,提到了虚函数,纯虚函数这篇博客我想继续进一步来说一下虚函数和虚函数表在C++中,每一个含有虚函数的类,编译器都会为它啊做出一个虚函数表(通常叫做vtable),这个虚函数表里面的每个元素都是函数指针,每个元素(函数......
  • *【莫比乌斯反演】数表[SDOI2014]
    问题有一张\(N\timesN\)的数表(\(N=10^5\)),其第\(i\)行第\(j\)列(\(1\lei\len\),\(1\lej\lem\))的数值为能同时整除\(i\)和\(j\)的所有自然数之和。有T次询问,每次询问给定\(n,m,A\),计算数表(1,1)至(n,m)中不大于\(A\)的数之和(\(|A|\le10^9\))。每组数据输出一行一个整数......
  • 数据是用二进制数表示的
    提到计算机,可定会想到二进制。为什么计算机要是用二进制?本章就是来学习二进制的。计算机的内部是由IC【集成电路的简称】这种电子部件构成的,而二进制并不是专门为了计算机而发明的,计算机使用二进制只是与IC的特性相符合。二进制数的位数就是8的倍数【这是因为计算机处理信息的基......
  • 《程序是怎样跑起来的》——第2章 数据使用二进制数表示的
    一、程序的运行机制与二进制数的关系1、程序的运行机制:要想对程序的运行机制形成一个大致印象,就要了解信息(数据)在计算机内部是以怎样的形式来表现的,又是以怎样的方法进行运算的。2、二进制数的作用:在C和Java等高级语言编写的程序中,数值、字符串和图像等信息在计算机内部都是......
  • IIFE(立即执行函数表达式)
    IIFEIIFE(立即执行函数表达式)是一种在定义时立即执行的JavaScript函数。这种函数形式非常有用,特别是当需要创建一个新的作用域以避免污染全局作用域或需要执行一段代码但不希望这段代码之后再被引用或重用。(function(){//函数体})();/*或者箭头函数*/(()=>{//......
  • 第2章数据是二进制数表示的 总结
    1用二进制数表示计算机信息的原因计算机内部是由IC"这种电子部件构成的有的有数个乃至数百个引脚;有的则像插花用的针盘,引脚在IC内部并排排列着。IC的所有引脚,只有直流电压0V或5V两个状态。所以IC的一个引脚,只能表示两个状态。IC的这个特性,决定了计算机的信息数据只能用二进制......