CF1582G Kuzya and Homework 题解

CF1582G Kuzya and Homework

首先容易知道的是 “仅含整数” 要想到分解质因数,然后 */ 两种运算容易想到对每个质因数的出现次数进行 +11 的操作。于是一个区间合法当且仅当所有质因数在这个区间内所有位置的出现次数大于等于 0

然后考虑求一个数组 li,表示如果以第 i 个位置作为区间右端点,那么区间左端点至少要 li。先假设我们已经求出了这个数组,现在要统计答案,那么一个区间 [L,R] 符合要求当且仅当 i[L,R],Lli,即 LminLiRli。要统计这个东西最简单的方法应该是单调栈,从大到小枚举 i,统计其作为左端点时的答案。保持单调栈内只包含使得 lj<i 的所有 j,对于所有 ljij 都是合法的,应当计入答案。但 j 合法不一定代表 i 能真正地作为左端点,只有 li=ii 才能作为左端点,并把刚才统计的答案算入总答案。具体实现不难,可以参考代码。

现在的问题是求 l 数组。其实这是容易的,先对所有 ai 分解质因数,对每一个质数维护其出现的位置。如果遇到 * 就把当前位置加入 ai 所有质因数的集合,如果遇到 / 就统计 ai 的所有质因数最后一次出现位置的最小值。考虑这样做的复杂度,显然时间和空间都不超过 O(nlogn),可以通过。

#include<bits/stdc++.h>
using namespace std;

constexpr int MAXN=2e6+5;
int n,a[MAXN],l[MAXN],mp[MAXN];
string s;
bool isp[MAXN];
vector<int>pri;
vector<int>pos[MAXN];
long long ans;
pair<int,int>stk[MAXN];
int top;

void prime(){
	for(int i=2;i<MAXN;i++){
		if(!isp[i]) pri.emplace_back(i),mp[i]=i;
		for(long long j:pri){
			if(i*j>=MAXN) break;
			isp[i*j]=1;
			mp[i*j]=j;
			if(i%j==0) break;
		}
	}
}

int main(){
	cin.tie(nullptr)->sync_with_stdio(0);
	cin>>n;
	prime();
	for(int i=1;i<=n;i++) cin>>a[i];
	cin>>s;
	s=' '+s;
	for(int i=1;i<=n;i++){
		l[i]=i;
		if(s[i]=='*'){
			int now=a[i];
			while(now>1){
				pos[mp[now]].emplace_back(i);
				now/=mp[now];
			}
		}else{
			int now=a[i];
			while(now>1){
				if(pos[mp[now]].empty()){
					l[i]=-1;
					break;
				}
				l[i]=min(l[i],pos[mp[now]].back());
				pos[mp[now]].pop_back();
				now/=mp[now];
			}
		}
	}
    for(int i=n;i;i--){
    	long long res=1;
    	while(top&&l[stk[top].first]>=l[i]) res+=stk[top--].second;
    	if(l[i]==i) ans+=res;
    	stk[++top]={i,res};
	}
	cout<<ans<<'\n';
	return 0;
}
posted @   Laoshan_PLUS  阅读(3)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】
点击右上角即可分享
微信分享提示