51Nod1190 最小公倍数之和 V2

题目看这里

繁衍反演真好玩

来看看这个题的式子

求Σlcm(i,b) (a<=i<=b)

首先不难发现以下这样的变换


让后做一下差分就得到答案,注意这题比较卡时间,需要比较好的优化

#pragma GCC opitmize("O3") #pragma G++ opitmize("O3") #include<stdio.h> #include<string.h> #include<algorithm> #define N 40010 #define LL long long #define M 1000000007 using namespace std; bool vis[N]; int t,w[N],n; inline LL F(LL i,int m){ return (i+i*m)*m>>1; } inline void rd(int& x){ char c=getchar(); x=0; for(;c>='0' && c<='9';c=getchar()) x=(x<<3)+(x<<1)+(c^48); } inline void out(int x){ char c[10]; int j=0; for(;x;x/=10) c[++j]=x%10; for(;j;--j) putchar(c[j]^48); putchar('\n'); } int init(){
    n=40000; for(int i=2;i<=n;++i){ if(!vis[i]) w[++t]=i; for(int j=1,k;j<=t&&(k=i*w[j])<=n;++j){
            vis[k]=1; if(i%w[j]==0) break; } } } int p[210],k[210],cnt; struct D{ int d,mu; } d[10000]; inline bool c1(D x,D y){ return x.d<y.d; } inline void dfs(int i,int S,int m){ if(i>t){ d[++cnt]=(D){S,m}; return; }
    dfs(i+1,S,m);
    dfs(i+1,S*=p[i],-m); for(int j=2;j<=k[i];++j)
        dfs(i+1,S*=p[i],0); } inline LL cal(int m,int n){ int x=n; t=cnt=0; LL ans=0; for(int i=1;w[i]*w[i]<=n;++i) if(x%w[i]==0){
            p[++t]=w[i]; k[t]=0; for(;x%w[i]==0;x/=w[i]) ++k[t]; } if(x>1) p[++t]=x,k[t]=1;
    dfs(1,1,1); LL k;
    sort(d+1,d+1+cnt,c1); for(int i=1;i<=cnt;++i) if(d[i].mu) for(int j=1;(k=(LL)d[i].d*d[j].d)<=n&&j<=cnt;++j) if(n%k==0)
            ans=(ans+(d[i].mu>0?(F(d[i].d,n/k)-F(d[i].d,m/k)):(F(d[i].d,m/k)-F(d[i].d,n/k))))%M; return (ans+M)%M; } int main(){
    init(); int T; rd(T); for(int n,m;T--;out(cal(m-1,n)*n%M)) rd(m),rd(n); }