Luogu P6583 回首过去 题解

Posted zhu-chen

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Luogu P6583 回首过去 题解相关的知识,希望对你有一定的参考价值。

几亿年没更的博客终于想起来了。。。。

其实不是我不想更,而是太水了发上去不好意思。。。。

但是觉得不能辜负zzk巨佬的贴心教导,所以还是写一写吧,毕竟好久没认真学一个新算法了


题目链接


首先80分的部分不难想

对于每一个分母,除去2和5对答案都没有影响,当把所有2、5都除去之后统计即可,复杂度(O(n))

Code

#include<bits/stdc++.h>
#define int long long
using namespace std;

void file(string s){freopen((s+".in").c_str(),"r",stdin),freopen((s+".out").c_str(),"w",stdout);}
int read()
{
	int a=0,f=1;
	char ch=getchar();
	while(ch<‘0‘||ch>‘9‘){
		if(ch==‘-‘)f=-f;
		ch=getchar();
	}
	while(ch>=‘0‘&&ch<=‘9‘){
		a=a*10+ch-‘0‘;
		ch=getchar();
	}
	return a*f;
}

int n;
long long ans;

signed main()
{
//	file("");
	n=read();
	for(int i=1;i<=n;++i){
		int x=i;
		while(!(x%2))x/=2;
		while(!(x%5))x/=5;
		ans+=n/x;
	} 
	cout<<ans;
	return 0;
}

100分部分就用了一些技巧

首先把这一部分

while(!(x%2))x/=2;
while(!(x%5))x/=5;
ans+=n/x;

改成

if(i%2==0||i%5==0)continue;
for(int j=1;j*i<=n;j*=2){
	for(int k=1;k*i*j<=n;k*=5){
		tmp++;
	}
}
ans+=n/l*tmp;

也就是把找每个数对应的数是多少变成了算每个数能对应多少个数

接下来我们就发现只要能优化tmp的求解我们就能进行优化了

这里用到了整除分块的思想,先把所有因子只有2和5的数从小到大求出来

void build()
{
        for(int i=1;i<=n;i*=2){
	      for(int j=1;j*i<=5;j*=5){
			num[++tot]=i*j;
		}
	}
	sort(num+1,num+1+tot);
}

那么对于每一个tmp对应的是哪一个区间就可以求出来

最后用容斥求一下每个区间有多少个需要统计的数(就是没有被continue的数)就可以了

复杂度(O(sqrt n))

Code:

#include<bits/stdc++.h>
#define int long long
using namespace std;

void file(string s){freopen((s+".in").c_str(),"r",stdin),freopen((s+".out").c_str(),"w",stdout);}
int read()
{
	int a=0,f=1;
	char ch=getchar();
	while(ch<‘0‘||ch>‘9‘){
		if(ch==‘-‘)f=-f;
		ch=getchar();
	}
	while(ch>=‘0‘&&ch<=‘9‘){
		a=a*10+ch-‘0‘;
		ch=getchar();
	}
	return a*f;
}

int n,ans,tot;
int num[5000];

void build()
{
	for(int i=1;i<=n;i*=2){
		for(int j=1;j*i<=n;j*=5){
			num[++tot]=i*j;
		}
	}
	sort(num+1,num+1+tot);
}

int makenum(int l,int r)
{
	l--;
	int num2,num5,num10;
	num2=r/2-l/2;
	num5=r/5-l/5;
	num10=r/10-l/10;
	return r-l-num2-num5+num10;
}

int makef(int x)
{
	return upper_bound(num+1,num+tot+1,x)-num-1;
}

signed main()
{
//	file("");
	n=read();
	build();
	for(int l=1,r;l<=n;l=r+1){
		r=n/(n/l);
		ans+=makef(n/l)*makenum(l,r)*(n/l);
	}
	cout<<ans;
	return 0;
}

orz zzk!

以上是关于Luogu P6583 回首过去 题解的主要内容,如果未能解决你的问题,请参考以下文章

luogu P6583 回首过去 简单数论变换 简单容斥

luogu P6583 回首过去 简单数论变换 简单容斥

题解Luogu1739 表达式括号匹配

题解入阵曲 luogu3941 前缀和 压维

回首过去,展望未来

(luogu题解搬运系列)luogu p2651 添加括号Ⅲ