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

Posted chdy

tags:

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

LINK:回首过去

考试的时候没推出来 原因:状态真的很差 以及 数论方面的 我甚至连除数分块都给忘了.

手玩几个数据 可以发现 (frac{x}{y})满足题目中的条件当且仅当 这个是一个既约分数 且 y中只含2,5的因子.

枚举y考虑贡献 先除掉本身的2,5的倍数后变成w1 之后考虑x 1~n中x只要是w1的倍数那么都是不合法的。

把这些数给去掉即可.这样就得到了一个O(n)的做法。

观察数据范围 容易想到 考察的是一个根号的算法。

此时考虑枚举w1 那么可以发现w1要满足 不是2,5的倍数 此时贡献为n/w1 考虑这样的数字有多少个 容易发现可以暴力统计 强行乘上若干个2和若干个5.

推到这里我昨天卡住了 因为这还没有达到很好的效果 忘了整除分块了 直接分块 容易得到一个(sqrt{n}log_2log_5)的做法。

不过这样 只能信仰过题。考虑把两个log优化掉 可以发现求多少个的时候其实是求 1~n/w1中 只包含2,5质因子数的个数。

将这个东西预处理 然后从小到大排序 整除分块的时候 就可以单调的判断了 复杂度(sqrt{n}+log^3)

中间一个小步骤需要简单容斥一下.

const ll MAXN=10010;
ll n,ans,cnt;
ll a[MAXN];
inline ll calc(ll x)
{
	return x-x/2-x/5+x/10;
}
signed main()
{
	//freopen("1.in","r",stdin);
	get(n);
	for(ll i=1;i<=n;i=i*2)
		for(ll j=1;i*j<=n;j=j*5)a[++cnt]=i*j;
	sort(a+1,a+1+cnt);
	ll w1,w2,flag=cnt;
	for(ll i=1;i<=n;i=w2+1)
	{
		w1=n/i;w2=n/w1;
		while(a[flag]>w1&&flag)--flag;
		ans=ans+w1*(calc(w2)-calc(i-1))*flag;
	}
	putl(ans);
	return 0;
}

以上是关于luogu P6583 回首过去 简单数论变换 简单容斥的主要内容,如果未能解决你的问题,请参考以下文章

Luogu P6583 回首过去 题解

Luogu P6583 回首过去 题解

luogu2312 解方程 (数论,hash)

Luogu5307 [COCI2019] Mobitel 数论分块递推

[luogu2054 AHOI2005] 洗牌 (数论)

NTT(快速数论变换)用到的各种素数及原根