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;
}
posted @ 2020-05-31 14:38  chdy  阅读(276)  评论(0编辑  收藏  举报