P5071 [Ynoi Easy Round 2015] 此时此刻的光辉 题解?
前言
- 再次占领一页!

- \(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
$\mathscr{msjing}$

浙公网安备 33010602011771号