【与因数有关的问题】

【与因数有关的问题】

【注意点】

d(m):表示m的因数
可用于求复杂度:比O(logn)小
求因数可避免遍历整个n->优化点

因数表

\(O(nlogn)\),适合xe5的范围

void init(){
    for(int i=1;i<=3e5;i++){
        for(int j=1;j*i<=3e5;j++){
            g[j*i].push_back(i);
        }
    }
    for(int i=2;i<=3e5;i++){
        sort(g[i].begin(),g[i].end(),cmp<int>);
    }
}

Modular Sorting

https://codeforces.com/contest/2123/problem/G

题目大意

image

思路

【思路】
设d=gcd(k,m) 那么d应该都是m的因数->可以预处理所有的因数
对于一个数列A (a[i]+k*cnt)%m都是d的倍数

容易想到O(nq)做法:判断下一个数能不能比上一个数大

优化:(常见思路)插入操作同步处理 使得查询为O(1)
->考虑在d的情况下 需要+多少次->最多操作m/d-1次 超过了肯定不满足
->当a[i]%d>a[i+1]%d时需要操作

->对于每一个修改操作:只会改变a[i-1],a[i]和a[i+1],a[i]的关系
->查询操作:查cnt[d]即可

image

代码

#include<bits/stdc++.h>
using namespace std;
#define endl '\n'
#define whiteink signed main
#define fi first
#define sc second

using i64=long long;
using i128=__int128;
typedef pair<int,int> PII;
typedef pair<i64,i64> P64;
i64 max64(i64 a,i64 b){return a>b?a:b;}
i64 min64(i64 a,i64 b){return a<b?a:b;}
bool cmp(int a,int b){return a>b;}
bool cmp64(i64 a,i64 b){return a>b;}

const int inf_int=0x3f3f3f3f;
const i64 inf_i64=0x3f3f3f3f3f3f3f3f;
const i64 mod=1e9+7;

//快速幂
i64 qmi(i64 a,i64 k,i64 p){
	i64 res=1LL;
	while(k){
		if(k&1LL) res=res*a%p;
		k>>=1LL;//删去k的末位 
		a=a*a%p;
	}
	return res;
}

//gcd
i64 gcd(i64 a,i64 b){
	return b?gcd(b,a%b):a;
}

//求约数
vector<int> get_divisors(int qaq){
	vector<int> res;
	for(int i=1;i<=qaq/i;i++){
		if(qaq%i==0){
			res.push_back(i);
			if(i!=qaq/i) res.push_back(qaq/i);
		}
	}
	sort(res.begin(),res.end());
	return res;
}

const int N=3e5+10;
int n,m,q;
vector<vector<int>> fac;
//找所有因数
void init(int x){
	fac.resize(x+1);
	for(int i=1;i<=x;i++){
		for(int j=i;j<=x;j+=i){
			fac[j].push_back(i);
		}
	}
}
void solve(){
    cin>>n>>m>>q;
	vector<int> a(n+1,0);
	int k=fac[m].size();
	map<int,int> cnt;
	for(int i=1;i<=n;i++) cin>>a[i];
	for(int i=1;i<n;i++){
		for(auto d:fac[m]){
			if(a[i]%d>a[i+1]%d) cnt[d]+=1;
		}
	}
	while(q--){
		int op;
		cin>>op;
		if(op==1){
			int i,x;
			cin>>i>>x;
			if(i>1){
				for(auto d:fac[m]){
					if(a[i-1]%d>a[i]%d) cnt[d]-=1;
				}
				for(auto d:fac[m]){
					if(a[i-1]%d>x%d) cnt[d]+=1;
				}
			}
			if(i<n){
				for(auto d:fac[m]){
					if(a[i]%d>a[i+1]%d) cnt[d]-=1;
				}
				for(auto d:fac[m]){
					if(x%d>a[i+1]%d) cnt[d]+=1;
				}
			}
			a[i]=x;
		}
		else if(op==2){
			int kk;
			cin>>kk;
			int d=gcd(kk,m);
			if(cnt[d]>m/d-1){
				cout<<"NO"<<endl;
			}
			else{
				cout<<"YES"<<endl;
			}
		}
	}
}

whiteink(){
    ios::sync_with_stdio(0);
    cin.tie(0);
    cout.tie(0);
	init(5e5);
    //precompute();

    int T=1;
    cin>>T;
    while(T--) solve();
    return 0;
}

Product Queries

https://codeforces.com/contest/2193/problem/E

与因数有关的DP

题目大意

给定数集 a ,元素可重复选取。
1~n 的整数,对每个 i,求从 a 中选数(可重复)乘积恰好为 i 的最少元素个数,无法选取则输出 - 1。

思路

首先打出因数表
\(dp[i]\)为答案,从小到大更新状态,这样小状态必定更新。
遍历因数,有转移公式dp[i]=min(dp[i],dp[res]+dp[son])
初始化:如果该数存在,则dp[i]=1

AC代码

const int N=3e5+10;
int n;
vector<int> g[N];
void init(){
    for(int i=1;i<=3e5;i++){
        for(int j=1;j*i<=3e5;j++){
            g[j*i].push_back(i);
        }
    }
    for(int i=2;i<=3e5;i++){
        sort(g[i].begin(),g[i].end(),cmp<int>);
    }
}

void solve(){
    cin>>n;
    vector<int> a(n+1,0);
    for(int i=1;i<=n;i++) cin>>a[i];
    map<int,int> mp;
    for(int i=1;i<=n;i++) mp[a[i]]=1;
    vector<int> dp(n+1,1e9);
    dp[1]=0;
    for(int i=2;i<=n;i++){
    	if(mp[i]){
    		dp[i]=1;
    		continue;
    	}
        for(auto son:g[i]){
        	if(son==1) break;
            int res=i/son;
            if(dp[res]!=1e9){
                dp[i]=min(dp[i],dp[res]+dp[son]);//拆成这两个数所需的个数
            }
        }
    }
    for(int i=1;i<=n;i++) if(dp[i]==1e9) dp[i]=-1;
    if(!mp[1]) dp[1]=-1;
    else dp[1]=1;
    for(int i=1;i<=n;i++) cout<<dp[i]<<" ";
    cout<<endl;
}
posted @ 2025-07-05 15:26  White_ink  阅读(18)  评论(0)    收藏  举报