专栏文章
11 18
P14523题解参与者 1已保存评论 0
文章操作
快速查看文章及其快照的属性,并进行相关操作。
- 当前评论
- 0 条
- 当前快照
- 1 份
- 快照标识符
- @min5hn2h
- 此快照首次捕获于
- 2025/12/01 20:53 3 个月前
- 此快照最后确认于
- 2025/12/01 20:53 3 个月前
定义一个序列 合法,当且仅当对于任意一个质数 , (非严格)单峰。一个序列的权值 为,将这个序列的 改成任意值(包括 )使得序列合法,并且 不增的方案数。每次询问给定 ,求 。。
显然每个质因数独立。
结论:对于两个相邻的 使得 而言,对于任何一个 ,记录 为 在 上的指数,如果 ,则对于任意 ,,且序列递减。证明:显然 ,否则 为一个谷。,否则 ,要么存在一个 使得 且 ,此时 构成一个谷;要么存在一个 使得 且 ,此时 构成一个谷;否则没有一个原有的 使得 , 为全局最大值,此时违反 规则。
于是每一段的选择就独立了,就可以比较方便地进行统计了。具体地,对于一个 而言,假如一个长度为 的 连续段左右两侧的 值分别为 ,那么相当于是 个变量,和值为 的方案数。方案数为 。特别地,如果左右两边没有值的话 视为 。
具体地,我们考虑进行扫描线,然后维护 序列的权值,之后进行历史和。当 扫描到一个 的时候,设 为最大的数使得 全是 ,则只对 进行前缀积即可。否则,我们暴力更新所有 的 权值,然后对 进行前缀积。最后,设 为最大的数使得 本身权值不为 (即只保留非 位置后合法),对 区间乘以 。
最后再说一下怎么求最大的 使得 合法。
BC 性质:我们在往右扫描 的时候额外维护一个 ,满足 不降。当 时,,否则 。
B 性质:对于每一个质因子都跑一遍显然是不现实的。不过观察到对于 BC 版本而言,如果序列的末尾的 值为 ,那么再插一个 不会改变任何东西。所以在扫描 时只需要对 与 的质因子的并更新即可。
无特殊性质:我们改为维护最大的 使得 且 合法。令 为 左边最大的 使得 ,则真正的答案为 。特判前缀 。
设矩阵大小 ,则时间复杂度 。(使用矩阵维护历史和)
CPP#include<bits/stdc++.h>
using namespace std;
#define int long long
constexpr int mod=1e9+7;
constexpr int maxn=5e5;
void veradd(int& x,int y){
x+=y;
if(x>=mod){
x-=mod;
}
}
int add(int x,int y){
return x+y-(x+y>=mod)*mod;
}
namespace number{
constexpr int maxn=5e5;
int notprime[maxn+5];
int minprime[maxn+5];
vector<int> primes;
void init(){
notprime[1]=1;
minprime[1]=1;
for(int i=2;i<=maxn;i++){
if(!notprime[i]){
primes.push_back(i);
minprime[i]=i;
}
for(auto j:primes){
if(i*j>maxn){
break;
}
notprime[i*j]=1;
minprime[i*j]=j;
if(i%j==0){
break;
}
}
}
}
unordered_map<int,int> split(int x){
unordered_map<int,int> ans;
while(x!=1){
int p=minprime[x];
int cnt=0;
while(p==minprime[x]){
x/=p;
cnt++;
}
ans[p]=cnt;
}
return ans;
}
}
namespace historysum{
struct info{
int a[2];
info(){
a[0]=a[1]=0;
}
int& operator [](int x){
return a[x];
}
};
struct Matrix{
int a[2][2];
Matrix(){
a[0][0]=a[1][1]=1;
a[1][0]=a[0][1]=0;
}
int* operator [](int x){
return a[x];
}
};
Matrix Time(int x){
Matrix ret;
ret.a[0][0]=x;
return ret;
}
Matrix Sum(){
Matrix ret;
ret.a[0][1]=1;
return ret;
}
void operator *= (info& a,Matrix t){
info ret;
ret[0]=add(a[0]*t[0][0]%mod,a[1]*t[1][0]%mod);
ret[1]=add(a[0]*t[0][1]%mod,a[1]*t[1][1]%mod);
a=ret;
}
void operator += (info& a,info b){
veradd(a[0],b[0]);
veradd(a[1],b[1]);
}
info operator + (info a,info b){
info ret;
ret[0]=add(a[0],b[0]);
ret[1]=add(a[1],b[1]);
return ret;
}
Matrix operator * (Matrix a,Matrix b){
Matrix ret;
ret[0][0]=(a[0][0]*b[0][0]+a[0][1]*b[1][0])%mod;
ret[0][1]=(a[0][0]*b[0][1]+a[0][1]*b[1][1])%mod;
ret[1][0]=(a[1][0]*b[0][0]+a[1][1]*b[1][0])%mod;
ret[1][1]=(a[1][0]*b[0][1]+a[1][1]*b[1][1])%mod;
return ret;
}
info sum[(maxn<<2)+5];
Matrix tag[(maxn<<2)+5];
bool havetag[(maxn<<2)+5];
void build(int now,int from,int to){
sum[now][0]=to-from+1;
if(from==to)
return;
int mid=(from+to)>>1;
build(now<<1,from,mid);
build(now<<1|1,mid+1,to);
}
void on(int now,Matrix tg){
// cerr<<"Onbeg!: "<<now<<"\n";
sum[now]*=tg;
// cerr<<"Onmid!\n";
if(havetag[now]==0){
tag[now]=tg;
// cerr<<"Oned!:\n";
havetag[now]=1;
}
else{
tag[now]=tag[now]*tg;
}
}
void pushup(int now){
sum[now]=sum[now<<1]+sum[now<<1|1];
}
void pushdown(int now){
if(havetag[now]){
on(now<<1,tag[now]);
on(now<<1|1,tag[now]);
tag[now]=Matrix();
havetag[now]=0;
}
}
void update(int now,int from,int to,int ql,int qr,Matrix v){
// if(now==1){
// if(v[0][1]==0){
// cout<<"Update "<<ql<<" "<<qr<<" "<<v[0][0]<<"\n";
// }
// }
// cerr<<from<<" "<<to<<" "<<ql<<" "<<qr<<"\n";
if(from==ql&&to==qr){
on(now,v);
return;
}
pushdown(now);
// cerr<<"Pushdown!\n";
// exit(0);
int mid=(from+to)>>1;
if(ql<=mid){
update(now<<1,from,mid,ql,min(qr,mid),v);
}
if(mid<qr){
update(now<<1|1,mid+1,to,max(mid+1,ql),qr,v);
}
pushup(now);
}
info quary(int now,int from,int to,int ql,int qr){
if(from==ql&&to==qr){
return sum[now];
}
pushdown(now);
int mid=(from+to)>>1;
info ans;
if(ql<=mid){
//veradd(ans,quary(now<<1,from,mid,ql,min(mid,qr)));
ans+=quary(now<<1,from,mid,ql,min(mid,qr));
}
if(mid<qr){
ans+=quary(now<<1|1,mid+1,to,max(mid+1,ql),qr);
}
return ans;
}
}
namespace Binom{
constexpr int Bmax=maxn+50;
int frac[Bmax+5];
int ifrac[Bmax+5];
int ksm(int a,int b){
int ans=1;
while(b){
if(b&1){
ans=ans*a%mod;
}
a=a*a%mod;
b>>=1;
}
return ans;
}
void init(){
frac[0]=1;
for(int i=1;i<=Bmax;i++){
frac[i]=frac[i-1]*i%mod;
}
ifrac[Bmax]=ksm(frac[Bmax],mod-2);
for(int i=Bmax-1;i>=0;i--){
ifrac[i]=ifrac[i+1]*(i+1)%mod;
}
}
int binom(int n,int m){
return frac[n]*ifrac[n-m]%mod*ifrac[m]%mod;
}
}
using Binom::binom;
using historysum::update;
using historysum::Time;
using historysum::Sum;
using historysum::update;
using number::split;
using historysum::quary;
using Binom::ksm;
int n,q;
int a[maxn+5];
int lft[maxn+5];
int answer[maxn+5];
unordered_map<int,int> splited[maxn+5];
vector<pair<int,int>> quarys[maxn+5];
int lownotdown[maxn+5];
int llim[maxn+5];
int Mx=1;
void calc(int v,int pos,int lastpos){
int lastv=splited[lastpos].count(v)?splited[lastpos][v]:0;
int nowv=splited[pos].count(v)?splited[pos][v]:0;
if(nowv>lastv){
// cout<<pos<<":"<<v<<" Update! "<<nowv<<" "<<lastv<<"\n";
Mx=max(Mx,lownotdown[v]);
}
else if(nowv<lastv){
lownotdown[v]=pos;
}
}
signed main(){
ios::sync_with_stdio(0);
cin.tie(nullptr);
Binom::init();
number::init();
cin>>n>>q;
historysum::build(1,1,n);
for(int i=1;i<=n;i++){
cin>>a[i];
if(a[i]==1){
lft[i]=lft[i-1];
}
else{
lft[i]=i;
}
}
for(int i=1;i<=n;i++){
splited[i]=split(a[i]);
}
for(int i=1;i<=q;i++){
int l,r;
cin>>l>>r;
quarys[r].push_back(make_pair(l,i));
}
llim[0]=1;
for(int i=1;i<=n;i++){
if(a[i]==1){
llim[i]=llim[i-1];
}
else{
int lpos=lft[i-1];
if(lpos==0){
for(int j=1;j<=maxn;j++){
lownotdown[j]=i;
}
Mx=i;
}
else{
for(auto j:splited[lpos]){
calc(j.first,i,lpos);
}
for(auto j:splited[i]){
if(!splited[lpos].count(j.first)){
calc(j.first,i,lpos);
}
}
}
llim[i]=lft[Mx-1]+1;
}
}
for(int i=1;i<=n;i++){
// cerr<<i<<":\n";
if(a[i]==1){
if(lft[i]){
int old=1,New=1;
for(auto j:splited[lft[i]]){
if(lft[i]!=i-1)
old=old*binom(j.second+i-lft[i]-1,i-lft[i]-1)%mod;
New=New*binom(j.second+i-lft[i],i-lft[i])%mod;
}
update(1,1,n,1,lft[i],Time(New*ksm(old,mod-2)%mod));
}
}
else{
int tail=i-1;
while(a[tail]==1){
int bl=1;
for(auto j:splited[i]){
bl=bl*binom(j.second+i-tail,i-tail)%mod;
}
update(1,1,n,tail,tail,Time(bl));
tail--;
}
if(lft[i-1]){
if(a[i-1]==1){
int bl=1;
for(auto j:splited[lft[i-1]]){
bl=bl*binom(j.second+i-lft[i-1]-1,i-lft[i-1]-1)%mod;
}
bl=ksm(bl,mod-2);
for(auto j:splited[lft[i-1]]){
int ben=j.second,other=(splited[i].count(j.first)?splited[i][j.first]:0);
bl=bl*binom(abs(ben-other)+i-lft[i-1]-1,i-lft[i-1]-1)%mod;
}
for(auto j:splited[i]){
if(!splited[lft[i-1]].count(j.first)){
bl=bl*binom(j.second+i-lft[i-1]-1,i-lft[i-1]-1)%mod;
}
}
update(1,1,n,1,lft[i-1],Time(bl));
}
}
}
if(llim[i]!=1){
update(1,1,n,1,llim[i]-1,Time(0));
}
update(1,1,n,1,i,Sum());
for(auto j:quarys[i]){
answer[j.second]=quary(1,1,n,j.first,i)[1];
}
}
for(int i=1;i<=q;i++){
cout<<answer[i]<<"\n";
}
}
相关推荐
评论
共 0 条评论,欢迎与作者交流。
正在加载评论...