专栏文章

11 18

P14523题解参与者 1已保存评论 0

文章操作

快速查看文章及其快照的属性,并进行相关操作。

当前评论
0 条
当前快照
1 份
快照标识符
@min5hn2h
此快照首次捕获于
2025/12/01 20:53
3 个月前
此快照最后确认于
2025/12/01 20:53
3 个月前
查看原文
定义一个序列 {an}\{a_n\} 合法,当且仅当对于任意一个质数 pp{gcd(an,p+}}\{\gcd(a_n,p^{+\infty}\}\} (非严格)单峰。
一个序列的权值 f(a)f(a) 为,将这个序列的 11 改成任意值(包括 11)使得序列合法,并且 lcm\operatorname{lcm} 不增的方案数。
每次询问给定 [L,R][L,R],求 [l,r][L,R]f(a[l:r])\sum \limits_{[l,r] \in [L,R]} f(a[l:r])
n2×105n \le 2 \times 10^5
显然每个质因数独立。
结论:对于两个相邻的 i,ji,j 使得 ai1,aj1a_i \neq 1,a_j \neq 1 而言,对于任何一个 pp,记录 viv_iaia_ipp 上的指数,如果 vivjv_i \ge v_j,则对于任意 i<k<ji<k<jvk[vj,vi]v_k \in [v_j,v_i],且序列递减。
证明:显然 vkvjv_k \ge v_j,否则 i,k,ji,k,j 为一个谷。vkviv_k \le v_i,否则 vk>vi,vk>vjv_k>v_i,v_k>v_j,要么存在一个 tt 使得 t<it<ivt>viv_t>v_i,此时 t,i,kt,i,k 构成一个谷;要么存在一个 tt 使得 t>jt>jvt>vjv_t>v_j,此时 k,j,tk,j,t 构成一个谷;否则没有一个原有的 tt 使得 vtvj<vkv_t \le v_j < v_kkk 为全局最大值,此时违反 lcm\operatorname{lcm} 规则。
于是每一段的选择就独立了,就可以比较方便地进行统计了。具体地,对于一个 pp 而言,假如一个长度为 ll11 连续段左右两侧的 vv 值分别为 a,ba,b,那么相当于是 l+1l+1 个变量,和值为 ab|a-b| 的方案数。方案数为 (ab+ll)\binom{|a-b|+l}{l}。特别地,如果左右两边没有值的话 vv 视为 00
具体地,我们考虑进行扫描线,然后维护 [i,r][i,r] 序列的权值,之后进行历史和。当 rr 扫描到一个 11 的时候,设 ii 为最大的数使得 (i,r](i,r] 全是 11,则只对 [1,i][1,i] 进行前缀积即可。否则,我们暴力更新所有 j(i,r)j \in (i,r)[j,r][j,r] 权值,然后对 [1,i][1,i] 进行前缀积。最后,设 vv 为最大的数使得 (v,r](v,r] 本身权值不为 00(即只保留非 11 位置后合法),对 [1,v)[1,v) 区间乘以 00
最后再说一下怎么求最大的 vv 使得 (v,r](v,r] 合法。
BC 性质:我们在往右扫描 rr 的时候额外维护一个 pp,满足 [p,r][p,r] 不降。当 vr+1>vrv_{r+1}>v_r 时,anspans \leftarrow p,否则 pr+1p \leftarrow r+1
B 性质:对于每一个质因子都跑一遍显然是不现实的。不过观察到对于 BC 版本而言,如果序列的末尾的 vv 值为 00,那么再插一个 00 不会改变任何东西。所以在扫描 rr 时只需要对 rrr1r-1 的质因子的并更新即可。
无特殊性质:我们改为维护最大的 vv 使得 av1a_v \neq 1[v,r][v,r] 合法。令 lstilst_iii 左边最大的 pp 使得 ap1a_p \neq 1,则真正的答案为 lstv1lst_{v-1}。特判前缀 11
设矩阵大小 K=2K=2,则时间复杂度 O(K3nlogn)O(K^3n \log n)。(使用矩阵维护历史和)
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 条评论,欢迎与作者交流。

正在加载评论...