P5071 [Ynoi Easy Round 2015] 此时此刻的光辉 题解?

前言

  • 再次占领一页!

2026-08-22 20-34-54屏幕截图

  • \(0 \to 18 \to 6 \to 54 \to 70 \to 100\)
  • 累似了
  • 话说 Super_lollipop 怎么做到常数这么小的就跑 \(7s\) tql
  • Super_lollipop:你这不是暴力吗你根号分治一下
  • msjing:不想改。。。

solve

  • 前置芝士:\(\mathcal{Pollard-Rho}\)
  • 不会可以看我这篇
  • 不过听说根号拆也能过而且飞快?

\(18pts\)

  • 我们直接暴力做,发现根本做不了
  • 要求求乘积约数个数,我们先介绍一个定理

约数个数定理:将一个数分解质因数后,各质因数的指数分别加 \(1\),再将所得结果相乘,其积即为该数的约数个数

  • 乘法原理可证
  • 这样我们就可以转化成求这个乘积的各质因数个数了
  • 而一个数的因数个数是可以拆开算的,就转化成了求每个 \(a\) 的各质因数个数了,而在 \(10^9\) 内一个数质因数种类不超过 \(10\),可以接受
  • 先求出每个数的各质因数及个数,由于根号求有点慢,所以用 \(\mathcal{Pollard-Rho}\),全部离散化,用莫队维护序列,扩展时加上被扩展数的各质因数个数,用一个桶记,收缩时减去,进行莫队操作后统计答案
点击查看代码
for (int i=1;i<=m;i++)
    {
        while (l>q[i].l) add(--l);
        while (r<q[i].r) add(++r);
        while (l<q[i].l) del(l++);
        while (r>q[i].r) del(r--);
        int as=1;
        for (int j=1;j<=id;j++)
            if (num[j]) (as*=(num[j]+1))%=p;
        ans[q[i].id]=as%p;
    }
  • 获得 \(18pts\)

\(6pts\)

  • 你发现可以每次新加一个数,其贡献为:\(as \>÷\> num \times (num + 1)\)
  • 其中,\(as\) 为加入这个数之前的答案,\(num\) 为加入这个数之前这个数的个数
  • 这个挺显然,因为加入这个数之后,会多一份除这个数外的其他数的贡献
  • 举个例子,现在有 \(x = 2^2 \times 3^2 \times 5^1\),约数有 \(ans = (2 + 1) \times (2 + 1) \times (1 + 1) = 18\),加入一个 \(5\),变成 $ x = 2^2 \times 3^2 \times 5^2$,约数有 \(ans = (2 + 1) \times (2 + 1) \times (2 + 1) = 27\),多了 \((2 + 1) \times (2 + 1) = 9\) 的贡献
  • 所以你可以在莫队里直接改贡献,就按上面改
  • 记得算逆元因为是在取模意义下
点击查看代码
int as=1;
int power(int x,int y)
{
    int res=1;
    while (y)
    {
        if (y&1) (res*=x)%=p;
        (x*=x)%=p;
        y>>=1;
    }
    return res;
}
int inv(int x) {return power(x,p-2);}
void add(int x)
{
    for (int i:fuc[x])
    {
        as=as*(num[i]+2)%p*inv((num[i]+1))%p;
        num[i]++;
    }
}
void del(int x)
{
    for (int i:fuc[x])
    {
        num[i]--;
        as=as*(num[i]+1)%p*inv((num[i]+2))%p;
    } 
}
  • 获得 \(6pts\)

\(54pts\)

  • 欸不对啊这这这为啥比刚才还低
  • 因为求逆元会带一只老哥,非常劣
  • 所以我们预处理逆元,不与莫队复杂度交叉
点击查看代码
int as=1;
int power(int x,int y)
{
    int res=1;
    while (y)
    {
        if (y&1) (res*=x)%=p;
        (x*=x)%=p;
        y>>=1;
    }
    return res;
}
int iv(int x) {return power(x,p-2);}
int inv[maxn];
void iit()
{
    for (int i=1;i<=maxn-10;i++)
        inv[i]=iv(i);
}
void add(int x)
{
    for (int i:fuc[x])
    {
        as=as*(num[i]+2)%p*inv[num[i]+1]%p;
        num[i]++;
    }
}
void del(int x)
{
    for (int i:fuc[x])
    {
        num[i]--;
        as=as*(num[i]+1)%p*inv[num[i]+2]%p;
    } 
}
  • 获得 \(54pts\)

\(70pts\)

  • 你发现莫队算贡献时,由于是一个一个数跳的,太慢了,需要优化一下
  • 那你可以让这个质因数直接算它的个数的贡献,而不是一个一个跳,算质因数时累计一下质因数个数就行,实现看看代码
点击查看代码
#include <bits/stdc++.h>
#define pb push_back
#define pii pair<int,int>
#define fi first
#define se second
#define int long long
using namespace std;
constexpr int maxn=4e6+10,p=19260817;
struct IO
{
	static const int Size=(1<<21);
	char buf[Size],*p1,*p2;
	int st[105],Top;
	~IO(){clear();}
	inline void clear(){fwrite(buf,1,Top,stdout);Top=0;}
	inline char gc(){return p1==p2&&(p2=(p1=buf)+fread(buf,1,Size,stdin),p1==p2)?EOF:*p1++;}
	inline void pc(const char c){Top==Size&&(clear(),0);buf[Top++]=c;}
	inline IO& operator >>(char& c){while(c=gc(),c==' ' || c=='\n' || c=='\r');return *this;}
	template<typename T>inline IO& operator >>(T& x)
    {
		x=0;bool f=0;char c=gc();
		while(!isdigit(c)){if(c=='-') f=1;c=gc();}
		while(isdigit(c)){x=(x<<1)+(x<<3)+(c^48);c=gc();}
		f?x=-x:0;
		return *this;
	}
	inline IO& operator >>(string& s)
    {
		s="";char c=gc();
		while(c==' ' || c=='\n' || c=='\r') c=gc();
		while(c!=' ' && c!='\n' && c!='\r' && c!=EOF) s+=c,c=gc();
		return *this;
	}
	inline IO& operator <<(const char c){pc(c);return *this;}
	template<typename T> inline IO& operator <<(T x)
    {
		if(x<0) pc('-'),x=-x;
		do st[++st[0]]=x%10,x/=10;while(x);
		while(st[0]) pc(st[st[0]--]+'0');
		return *this;
	}
	inline IO& operator <<(const string s){for(auto c:s) pc(c);return *this;}
	inline IO& operator <<(const char* c){for(int i=0;c[i];i++) pc(c[i]);return *this;}
} fin,fout;
int n,m;
int a[maxn];
struct _ {int l,r,id;}q[maxn];
namespace MR
{
    int A[13]={0,2,7,61};
    int power(int x,int y,int p)
    {
        int res=1;
        while (y)
        {
            if (y&1) (res*=x)%=p;
            (x*=x)%=p;
            y>>=1;
        }
        return res;
    }
    int chk(int p,int a)
    {
        int k=p-1,cnt=0;
        while (!(k&1))
            cnt++,k>>=1;
        int x=power(a,k,p);
        if (x == 1) return 1;
        for (int i=1;i<=cnt;i++)
        {
            if (x == p-1) return 1;
            (x*=x)%=p;
        }
        return 0;
    }
    int prime(int x)
    {
        for (int i=1;i<=3;i++)
        {
            if (x == A[i]) return 1;
            if (!(x%A[i])) return 0;
            if (!chk(x,A[i])) return 0;
        }
        return 1;
    }
}using namespace MR;
namespace PR
{
    mt19937 rd(time(0));
    int f(int x,int c,int p) {return (x*x%p+c)%p;}
    int _abs(int x,int y)
    {return x-y<0 ? y-x : x-y;}
    int gcd(int a,int b)
    {return b ? gcd(b,a%b) : a;}
    int rho(int n)
    {
        int mo=n;
        int x=rd()%mo,c=rd()%(mo-1)+1,p=1;
        for (int i=2,j=2,d=x;i;i++)
        {
            x=f(x,c,mo);
            (p*=_abs(x,d))%=mo;
            if (!(i%63) && gcd(p,n)!=1)
                return gcd(p,n);
            if (i == j)
            {
                j<<=1,d=x;
                if (gcd(p,n)!=1)
                    return gcd(p,n);
            }
        }
    }
    int pr(int n)
    {
        if (prime(n)) return n;
        int p=n;
        while (p == n) p=rho(n);
        return max(pr(p),pr(n/p));
    }
}using namespace PR;
vector<pii> fuc[maxn];
int L[maxn],R[maxn],pos[maxn],cnt,b;
void init()
{
    b=n/sqrt((long long)m)+1;
    cnt=n/b;
    for (int i=1;i<=cnt;i++)
        L[i]=R[i-1]+1,R[i]=b*i;
    if (R[cnt]<n)
        cnt++,L[cnt]=R[cnt-1],R[cnt]=n;
    for (int i=1;i<=cnt;i++)
        for (int j=L[i];j<=R[i];j++)
            pos[j]=i;
}
int col[maxn];
int num[maxn];
int as=1;
int power(int x,int y)
{
    int res=1;
    while (y)
    {
        if (y&1) (res*=x)%=p;
        (x*=x)%=p;
        y>>=1;
    }
    return res;
}
int iv(int x) {return power(x,p-2);}
int inv[maxn];
void iit()
{
    for (int i=1;i<=maxn-10;i++)
        inv[i]=iv(i);
}
void add(int x)
{
    for (pii i:fuc[x])
    {
        as=as*inv[num[i.fi]+1]%p*(num[i.fi]+i.se+1)%p;
        num[i.fi]+=i.se;
    }
}
void del(int x)
{
    for (pii i:fuc[x])
    {
        num[i.fi]-=i.se;
        as=as*(num[i.fi]+1)%p*inv[num[i.fi]+i.se+1]%p;
    }
}
int ans[maxn];
int ff[maxn],ct;
signed main()
{
    fin >> n >> m;
    iit();
    for (int i=1;i<=n;i++) fin >> a[i];
    for (int i=1;i<=m;i++)
    {
        int l,r;fin >> l >> r;
        q[i]={l,r,i};
    }
    for (int i=1;i<=n;i++)
    {
        int c=a[i];
        int k=pr(c);
        int lt=0;
        while (k!=c)
        {
            ff[++ct]=k;
            if (fuc[i].size())
            {
                if (lt == k)
                    fuc[i].back().se++;
                else fuc[i].pb({k,1});
            }
            else fuc[i].pb({k,1});
            lt=k;
            c/=k,k=pr(c);
        }
        ff[++ct]=k;
        if (fuc[i].size())
        {
            if (lt == k)
                fuc[i].back().se++;
            else fuc[i].pb({k,1});
        }
        else fuc[i].pb({k,1});
    }
    sort(ff+1,ff+1+ct);
    int id=unique(ff+1,ff+1+ct)-ff-1;
    for (int i=1;i<=n;i++)
        for (pii &j:fuc[i])
            j.fi=lower_bound(ff+1,ff+1+id,j.fi)-ff;
    // for (int i=1;i<=n;i++)
    //     for (pii j:fuc[i])
    //         cerr << i << ":" << j.fi << " " << j.se << endl;
    init();
    sort(q+1,q+1+m,[](_ a,_ b)
    {
        return pos[a.l] == pos[b.l] ? 
        (pos[a.l]&1) ? a.r<b.r : a.r>b.r : 
        pos[a.l]<pos[b.l];
    });
    int l=1,r=0;
    for (int i=1;i<=m;i++)
    {
        while (l>q[i].l) add(--l);
        while (r<q[i].r) add(++r);
        while (l<q[i].l) del(l++);
        while (r>q[i].r) del(r--);
        ans[q[i].id]=as%p;
    }
    for (int i=1;i<=m;i++)
        fout << ans[i] << '\n';
    return 0;
}
  • 这样你复杂度才是正确的,不过会挂一个奇大无比的常数
  • 获得 \(70pts\)

\(100pts\)

  • 欸,为啥没过,卡卡常,调调阈值,看看金石后任
  • 欸,这个用时咋不动啊
  • (你打开了题解)
  • 哦原来是 \(\mathcal{PR}\) 太慢了
  • 其实msjing在写 \(70pts\) 和 \(54pts\) 代码对拍找错时就发现了但是当时认为是数据生成假了qwq
  • 题解强强拜谢题解
  • 所以我们直接枚举 \(1000\) 之内的所有质因数(\(168\) 个),在算 \(\mathcal{PR}\) 先干掉这些,然后由于剩的很少了所以你的 \(\mathcal{PR}\) 可以跑的飞快
  • 事实上你可以只用莫队跑剩下的质因数,然后前面的前缀优化就能飞快,但是msjing不想改了hhh
  • 其实题解说的也是根号分治,但是msjing没看完就跑路了
点击查看代码
for (int i=1;i<=n;i++)
{
    int c=a[i];
    for (int j=1;j<=168;j++)
    {
        if (!(c%pri[j]))
        {
            int cnt=0;
            while (!(c%pri[j]))
                cnt++,c/=pri[j];
            ff[++ct]=pri[j];
            fuc[i].pb({pri[j],cnt});
        }
    }
    if (c == 1) continue;
    int k=pr(c);
    int lt=0;
    while (k!=c)
    {
        ff[++ct]=k;
        if (lt == k)
            fuc[i].back().se++;
        else fuc[i].pb({k,1});
        lt=k;
        c/=k,k=pr(c);
    }
    ff[++ct]=k;
    if (lt == k)
        fuc[i].back().se++;
    else fuc[i].pb({k,1});
}
sort(ff+1,ff+1+ct);
int id=unique(ff+1,ff+1+ct)-ff-1;
for (int i=1;i<=n;i++)
    for (pii &j:fuc[i])
        j.fi=lower_bound(ff+1,ff+1+id,j.fi)-ff;
  • 获得 \(100pts\)
  • 总复杂度 \(O(n \log n + n \sqrt n)\),带一个奇大的常数

总代码

石山警告
#include <bits/stdc++.h>
#define pb push_back
#define pii pair<int,int>
#define fi first
#define se second
#define int long long
using namespace std;
constexpr int maxn=1e6+10,p=19260817;
struct IO
{
	static const int Size=(1<<21);
	char buf[Size],*p1,*p2;
	int st[105],Top;
	~IO(){clear();}
	inline void clear(){fwrite(buf,1,Top,stdout);Top=0;}
	inline char gc(){return p1==p2&&(p2=(p1=buf)+fread(buf,1,Size,stdin),p1==p2)?EOF:*p1++;}
	inline void pc(const char c){Top==Size&&(clear(),0);buf[Top++]=c;}
	inline IO& operator >>(char& c){while(c=gc(),c==' ' || c=='\n' || c=='\r');return *this;}
	template<typename T>inline IO& operator >>(T& x)
    {
		x=0;bool f=0;char c=gc();
		while(!isdigit(c)){if(c=='-') f=1;c=gc();}
		while(isdigit(c)){x=(x<<1)+(x<<3)+(c^48);c=gc();}
		f?x=-x:0;
		return *this;
	}
	inline IO& operator >>(string& s)
    {
		s="";char c=gc();
		while(c==' ' || c=='\n' || c=='\r') c=gc();
		while(c!=' ' && c!='\n' && c!='\r' && c!=EOF) s+=c,c=gc();
		return *this;
	}
	inline IO& operator <<(const char c){pc(c);return *this;}
	template<typename T> inline IO& operator <<(T x)
    {
		if(x<0) pc('-'),x=-x;
		do st[++st[0]]=x%10,x/=10;while(x);
		while(st[0]) pc(st[st[0]--]+'0');
		return *this;
	}
	inline IO& operator <<(const string s){for(auto c:s) pc(c);return *this;}
	inline IO& operator <<(const char* c){for(int i=0;c[i];i++) pc(c[i]);return *this;}
} fin,fout;
int n,m;
int a[maxn];
struct _ {int l,r,id;}q[maxn];
namespace MR
{
    int A[4]={0,2,7,61};
    inline int power(int x,int y,int p)
    {
        int res=1;
        while (y)
        {
            if (y&1) (res*=x)%=p;
            (x*=x)%=p;
            y>>=1;
        }
        return res;
    }
    inline int chk(int p,int a)
    {
        int k=p-1,cnt=0;
        while (!(k&1))
            cnt++,k>>=1;
        int x=power(a,k,p);
        if (x == 1) return 1;
        for (int i=1;i<=cnt;i++)
        {
            if (x == p-1) return 1;
            (x*=x)%=p;
        }
        return 0;
    }
    inline int prime(int x)
    {
        for (int i=1;i<=3;i++)
        {
            if (x == A[i]) return 1;
            if (!(x%A[i])) return 0;
            if (!chk(x,A[i])) return 0;
        }
        return 1;
    }
}using namespace MR;
namespace PR
{
    mt19937 rd(time(0));
    inline int f(int x,int c,int p) {return (x*x%p+c)%p;}
    inline int _abs(int x,int y)
    {return x-y<0 ? y-x : x-y;}
    int gcd(int a,int b)
    {return b ? gcd(b,a%b) : a;}
    inline int rho(int n)
    {
        int mo=n;
        int x=rd()%mo,c=rd()%(mo-1)+1,p=1;
        for (int i=2,j=2,d=x;i;i++)
        {
            x=f(x,c,mo);
            (p*=_abs(x,d))%=mo;
            if (!(i%63) && gcd(p,n)!=1)
                return gcd(p,n);
            if (i == j)
            {
                j<<=1,d=x;
                if (gcd(p,n)!=1)
                    return gcd(p,n);
            }
        }
    }
    int pr(int n)
    {
        if (prime(n)) return n;
        int p=n;
        while (p == n) p=rho(n);
        return max(pr(p),pr(n/p));
    }
}using namespace PR;
vector<pii> fuc[maxn];
int L[maxn],R[maxn],pos[maxn],cnt,b;
void init()
{
    b=n/sqrt((long long)m)+1;
    cnt=n/b;
    for (int i=1;i<=cnt;i++)
        L[i]=R[i-1]+1,R[i]=b*i;
    if (R[cnt]<n)
        cnt++,L[cnt]=R[cnt-1],R[cnt]=n;
    for (int i=1;i<=cnt;i++)
        for (int j=L[i];j<=R[i];j++)
            pos[j]=i;
}
int col[maxn];
int num[maxn];
int as=1;
inline int power(int x,int y)
{
    int res=1;
    while (y)
    {
        if (y&1) (res*=x)%=p;
        (x*=x)%=p;
        y>>=1;
    }
    return res;
}
inline int iv(int x) {return power(x,p-2);}
int inv[maxn];
void iit()
{
    for (int i=1;i<=maxn-10;i++)
        inv[i]=iv(i);
}
void add(int x)
{
    for (pii i:fuc[x])
    {
        as=as*inv[num[i.fi]+1]%p*(num[i.fi]+i.se+1)%p;
        num[i.fi]+=i.se;
    }
}
void del(int x)
{
    for (pii i:fuc[x])
    {
        num[i.fi]-=i.se;
        as=as*(num[i.fi]+1)%p*inv[num[i.fi]+i.se+1]%p;
    }
}
int ans[maxn];
int ff[maxn],ct;
int pri[170]={0,2,3,5,7,11,13,17,19,23,29,
31,37,41,43,47,53,59,61,67,71,
73,79,83,89,97,101,103,107,109,113,
127,131,137,139,149,151,157,163,167,173,
179,181,191,193,197,199,211,223,227,229,
233,239,241,251,257,263,269,271,277,281,
283,293,307,311,313,317,331,337,347,349,
353,359,367,373,379,383,389,397,401,409,
419,421,431,433,439,443,449,457,461,463,
467,479,487,491,499,503,509,521,523,541,
547,557,563,569,571,577,587,593,599,601,
607,613,617,619,631,641,643,647,653,659,
661,673,677,683,691,701,709,719,727,733,
739,743,751,757,761,769,773,787,797,809,
811,821,823,827,829,839,853,857,859,863,
877,881,883,887,907,911,919,929,937,941,
947,953,967,971,977,983,991,997};
signed main()
{
    // cerr << pri[168] << endl;
    fin >> n >> m;
    iit();
    for (int i=1;i<=n;i++) fin >> a[i];
    for (int i=1;i<=m;i++)
    {
        int l,r;fin >> l >> r;
        q[i]={l,r,i};
    }
    for (int i=1;i<=n;i++)
    {
        int c=a[i];
        for (int j=1;j<=168;j++)
        {
            if (!(c%pri[j]))
            {
                int cnt=0;
                while (!(c%pri[j]))
                    cnt++,c/=pri[j];
                ff[++ct]=pri[j];
                fuc[i].pb({pri[j],cnt});
            }
        }
        if (c == 1) continue;
        int k=pr(c);
        int lt=0;
        while (k!=c)
        {
            ff[++ct]=k;
            if (lt == k)
                fuc[i].back().se++;
            else fuc[i].pb({k,1});
            lt=k;
            c/=k,k=pr(c);
        }
        ff[++ct]=k;
        if (lt == k)
            fuc[i].back().se++;
        else fuc[i].pb({k,1});
    }
    sort(ff+1,ff+1+ct);
    int id=unique(ff+1,ff+1+ct)-ff-1;
    for (int i=1;i<=n;i++)
        for (pii &j:fuc[i])
            j.fi=lower_bound(ff+1,ff+1+id,j.fi)-ff;
    // for (int i=1;i<=n;i++)
    //     for (pii j:fuc[i])
    //         cerr << i << ":" << j.fi << " " << j.se << endl;
    init();
    sort(q+1,q+1+m,[](_ a,_ b)
    {
        return pos[a.l] == pos[b.l] ? 
        (pos[a.l]&1) ? a.r<b.r : a.r>b.r : 
        pos[a.l]<pos[b.l];
    });
    int l=1,r=0;
    for (int i=1;i<=m;i++)
    {
        while (l>q[i].l) add(--l);
        while (r<q[i].r) add(++r);
        while (l<q[i].l) del(l++);
        while (r>q[i].r) del(r--);
        ans[q[i].id]=as%p;
    }
    for (int i=1;i<=m;i++)
        fout << ans[i] << '\n';
    return 0;
}

后话

  • 其实这题不难,就是时间限制太恶心了
  • 看题解很简单但是msjing写了俩小时
  • 为数不多msjing会做的hhh
posted @ 2026-08-22 21:26  msjing  阅读(26)  评论(3)    收藏  举报