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;
}