首页 > 其他分享 >P3312 [SDOI2014]数表

P3312 [SDOI2014]数表

时间:2023-06-15 12:55:46浏览次数:32  
标签:lfloor le dfrac P3312 rfloor ch SDOI2014 数表 sum

[SDOI2014]数表

题目描述

有一张 \(n\times m\) 的数表,其第 \(i\) 行第 \(j\) 列(\(1\le i\le n\),\(1\le j\le m\))的数值为能同时整除 \(i\) 和 \(j\) 的所有自然数之和。给定 \(a\),计算数表中不大于 \(a\) 的数之和。

\(1\le n,m\le 10^5\),\(1\le Q\le 2\times 10^4\)。

思路点拨

我们先考虑对于两个数 \(i,j\) ,那些数才会同时整除 \(i,j\) 。显然这些数都是 \(i,j\) 的公约数。我们定义 \(f(x)\) 表示 \(x\) 的约数和,即 \(\sum_{d|x}d\) 。对于 \(i,j\) 而言,满足条件的数是 \(f(\gcd(i,j))\) 。这一点很好理解。我们先不看 \(a\) 的约束,那么答案就是:

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

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

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

\[\sum_{d=1}^n f(d) \sum_{k=1}^{\lfloor \frac{n}{d}\rfloor} \mu(k) \lfloor \dfrac{n}{dk}\rfloor \dfrac{m}{dk}\rfloor \]

我们;令 \(T=kd\) ,那么

\[\sum_{T=1}^n \lfloor \dfrac{n}{T}\rfloor \lfloor \dfrac{m}{T}\rfloor( \sum_{d|T} f(d)\mu(\dfrac{T}{d})) \]

这个式子需要富比尼定理优化。可以 \(O(\sqrt n)\) 计算,前提是后边括号内的式子在 \(O(n \log n)\) 的时间预处理。

但是这道题目还没有写完,因为有 \(a\) 的至于限制,所以原式准确表达为:

\[\sum_{T=1}^n \lfloor \dfrac{n}{T}\rfloor \lfloor \dfrac{m}{T}\rfloor( \sum_{d|T} f(d)\mu(\dfrac{T}{d})[f(d) \leqslant a]) \]

我们可以将全部的询问离线,按照 \(a\) 为关键字排序,每一次就将 \(f(x) \leqslant a\) 的 \(f(x)\) 从 \(0\) 设为它本身的值。我们富比尼定理计算上面的式子的时候,\(g(x)=\sum_{d|T} f(d)\mu(\dfrac{T}{d})\) ,需要以区间的形式计算,所以我们还需要使用一个树状数组来进行单点修改,区间加和。

时间复杂度 \(O(n \log^2 n+q\sqrt n \log n)\) 。

放出一份代码:

#include<bits/stdc++.h>
#define int long long
using namespace std;
inline int read(){
	int x=0,f=1;
	char ch=getchar();
	while(ch<'0'||ch>'9'){
		if(ch=='-') f=-f;
		ch=getchar(); 
	}
	while(ch>='0'&&ch<='9'){
		x=x*10+ch-'0';
		ch=getchar();
	}
	return x*f;
}
const int mod=(1ll<<31);
int T;
const int MAXN=1e5+10,N=1e5;
struct node{
	int x,id;
	bool friend operator<(const node &A,const node &B){
		return A.x<B.x;
	}
}f[MAXN];
struct getans{
	int x,y,w;
	int id;
	bool friend operator<(const getans &A,const getans &B){
		return A.w<B.w;
	} 
}q[MAXN];
int ans[MAXN],mu[MAXN];
bool vis[MAXN];
void init(){
	for(int i=1;i<=N;i++){
		f[i].id=i;
		mu[i]=1;
		for(int j=i;j<=N;j+=i)
			f[j].x+=i;
	}
	sort(f+1,f+N+1);
	for(int i=2;i<=N;i++){
		if(vis[i]) continue;
		mu[i]=-1;
		for(int j=i*2;j<=N;j+=i){
			vis[j]=1;
			mu[j]=-mu[j];
			if(j%(i*i)==0) mu[j]=0;
		}
	}
}
int g[MAXN];
int lowbit(int x){
	return x&(-x);
}
void add(int x,int w){
	for(int i=x;i<=N;i+=lowbit(i))
		g[i]=(g[i]+w)%mod;
}
int query(int x){
	int cnt=0;
	for(int i=x;i;i-=lowbit(i))
		cnt=(cnt+g[i])%mod;
	return cnt;
}
signed main(){
	init();
	int T=read();
	for(int i=1;i<=T;i++){
		q[i].x=read(),q[i].y=read(),q[i].w=read();
		q[i].id=i;
	}
	sort(q+1,q+T+1);
	int last=1;
	for(int i=1;i<=T;i++){
		while(last<=N&&f[last].x<=q[i].w){
			int d=f[last].id;
			for(int T=d;T<=N;T+=d)
				add(T,(f[last].x*mu[T/d]+mod*100)%mod);
			last++;
		}
		int l=1,r=0,n=q[i].x,m=q[i].y;
		while(l<=min(n,m)){
			r=min(n/(n/l),m/(m/l));
			ans[q[i].id]=(ans[q[i].id]+(query(r)-query(l-1)+mod)*(n/l)%mod*(m/l))%mod;
			l=r+1;
		}
	}
	for(int i=1;i<=T;i++) cout<<ans[i]<<endl;
	return 0; 
}

标签:lfloor,le,dfrac,P3312,rfloor,ch,SDOI2014,数表,sum
From: https://www.cnblogs.com/Diavolo/p/17482567.html

相关文章

  • 【C++】c++单继承、多继承、菱形继承内存布局(虚函数表结构)
    单继承:只有一个基类和一个派生类classBase{public:virtualvoidfun1(){cout<<"Base::func1()"<<endl;}virtualvoidfun2(){cout<<"Base::func2()"<<endl;}private:intb;......
  • C++多态虚函数表详解(多重继承、多继承情况)
    本文关键词:C++多态多继承多重继承虚函数表虚函数指针动态绑定概述:C++相对其他面向对象语言来说,之所以灵活、高效。很大程度的占比在于其多态技术和模板技术。C++虚函数表是支撑C++多态的重要技术,它是C++动态绑定技术的核心。本文章将着重图解虚函数表相关知识,在阅读本文......
  • 多态、虚函数表、底层实现、多重继承的问题及处理
    本文代码摘自 http://dwz.date/PST;视频解析:十分钟带你搞明白虚函数、虚表、多态的原理以及多重继承带来的问题_哔哩哔哩_bilibili1、多态:基类指针只能调用基类的成员函数,不能调用派生类的成员函数;如果在基类的成员函数前加上virtual关键字,把它声明为虚函数;基类指针就可以......
  • javascript函数声明和函数表达式
    JavaScript中定义函数最常用的方式是函数声明和函数表达式。这两种技术非常相似,有时甚至难以区分,但在后续章节中可以看到,它们之间还是存在着微妙的区别。JavaScript定义函数最基本方式是函数声明,如下图:正如你所见,每个函数声明以强制性的function开头,其后紧接着强制性的函数名,以及......
  • C++中的虚函数表实现机制——对于虚表的内存布局讲解得非常好
    C++中的虚函数表实现机制摘自:https://blog.twofei.com/496/前言大家都应该知道C++的精髓是虚函数吧?虚函数带来的好处就是:可以定义一个基类的指针,其指向一个继承类,当通过基类的指针去调用函数时,可以在运行时决定该调用基类的函数还是继承类的函数.虚函数是实现多态(......
  • (5)使用函数验证哥德巴赫猜想:任何一个不小于6的偶数均可表示为两个奇和。输入两个正整数
    #include<stdio.h>#include<math.h>intprime(intm){  inti;  if(m<2)    return0;  for(i=2;i<=sqrt(m);i++){    if(!(m%i))      return0;  }  return1;}intmain(){  intm,n,flag;  printf("Enterm,......
  • luogu P3308 [SDOI2014]LIS
    题面传送门涨知识了,第一次知道网络流删边不用全图重跑。首先我们先跑一个暴力dp,出\(f_i\)表示以\(i\)结尾的最长上升子序列长度。然后我们将其按照这个dp值分层,相......
  • 第二章 数据是用二进制数表示的
    第二章观后感想:     第二章主要讲解了二进制数、移位运算、逻辑运算。     计算机内部是由IC这种电子部件构成的,IC的一个引脚,只能表示两个状态。IC的......
  • 2.1 用二进制数表示计算机信息的原因
    在C和Java等高级语言编写的程序中,数值、字符串和图像等信息在计算机内部都是以二进制数值的形式来表现的。也就是说,只要掌握了使用二进制数来表示信息的方法及其运算机制,也......
  • 一种不会丢失精度的分数表示法
    我们都知道,C++中就算是精度最高的浮点数longdouble也会存在精度丢失的问题,那么我们该如何解决这个问题呢?高精度浮点数又显得过于夸张繁琐![](https://img-blog.csdnimg.c......