P9836 种树 题解

P9836 种树 题目链接

前言废话:不怎么打洛谷的比赛,昨天心血来潮,结果只看了 T1 且分配指数假了,或者说根本没深入思考,这下掉大分了。但个人感觉是很不错的题。


题意:给你一个序列 \(a\),并给你一个数 \(k\)。可以将其分解因数后乘到任意 \(a_i\) 上。求最大化的 \(a_i\) 因数个数的乘积。

看到因数个数不难想到质因数分解,分解后的质因子对因数个数的贡献是独立的。不难得到一个数的因数个数为:\(\displaystyle\prod_{i=1}^{n}(p_i+1)\) ,其中 \(p_i\) 为该数的质因子。

于是问题转换为:给长度为 \(n\) 的序列 \(a\) 和一个操作数 \(w\),每次操作可以使任意一个 \(a_i+1 \to a_i\)。可以重复选择同一位置,最多进行 \(k\) 次该操作。最大化 \(\displaystyle\prod_{i=1}^{n} a_i\)

这是一个经典的贪心问题。我找到了他在力扣上的题目。容易证明每次操作最小值是最优的。

证明:用 \(x,y(x\ge y)\) 来表示两个元素。

对于较大元素操作后的乘积为 \((x+1)y=xy+y\) 而对较小元素操作后的乘积为 \(x(y+1)=xy+x\)。两者相减得到 \(xy+y-xy-x=y-x\le0\),因此选择较小元素操作后的乘积更大。

最后由于 \(n\le1e4\) 所以怎么实现都行。虽然丑但相信大家能看懂我写的码,这个复杂度是 \(O(n\sqrt{n}\log{n})\)

这是我非常抽象且丑陋的码。问就是不想重构导致的。有空会重构一份的。

#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int N=1e4+5;
const int M=2e3+5;
const ll mod=998244353;
ll ans=1;
ll a[N];
int n,m;
int tong[N],vis[N],sum,r[N];
int ysm[110],cntm[N],pm;
int ys[N][110],p[N];
priority_queue<int>cnt[M];
void fj(int k,int x){
	int q=sqrt(x),y=x;
	for(int i=2;i<=q;i++){
		if(y%i==0){
			if(!vis[i]){
				vis[i]=1;
				tong[i]=++sum;
				r[sum]=i;
			}
			ys[k][++p[k]]=i;
			int tot=0;
			while(y%i==0){
				y/=i;
				tot++;
			}
			cnt[tong[i]].push(-tot);
		}
		if(y==1) break;
	}
	if(y>1){
		ys[k][++p[k]]=y;
		if(!vis[y]){
			vis[y]=1;
			tong[y]=++sum;
			r[sum]=y;
		}
		cnt[tong[y]].push(-1); 
	}
}
void fj2(){
	int q=sqrt(m),y=m;
	for(int i=2;i<=q;i++){
		if(y%i==0){
			if(!vis[i]){
				vis[i]=1;
				tong[i]=++sum;
				r[sum]=i;
			}
			ysm[++pm]=i;
			while(y%i==0){
				y/=i;
				cntm[tong[i]]++;
			}
		}
		if(y==1) break;
	}
	if(y>1){
		ysm[++pm]=y;
		if(!vis[y]){
			vis[y]=1;
			tong[y]=++sum;
			r[sum]=y;
		}
		cntm[tong[y]]=1;
	}
}
int main(){
	scanf("%d%d",&n,&m);
	for(int i=1;i<=n;i++){
		scanf("%lld",&a[i]);
		fj(i,a[i]);
	}
	fj2();
	for(int i=1;i<=sum;i++){
		int x=cnt[tong[r[i]]].size();
		for(int j=x;j<n;j++) cnt[tong[r[i]]].push(0);
	}
	for(int i=1;i<=pm;i++){
		int t=ysm[i];
		for(int j=1;j<=cntm[tong[t]];j++){
			int x=cnt[tong[t]].top();
			cnt[tong[t]].pop();
			x--;
			cnt[tong[t]].push(x);
		}
	}
	for(int i=1;i<=sum;i++)
		while(cnt[tong[r[i]]].size()){
			int x=-cnt[tong[r[i]]].top();
			cnt[tong[r[i]]].pop();
			ans=(ans*(x+1))%mod;
		}
	printf("%lld",ans);
	return 0;
}

或许你们看不见的 AC 记录

posted @ 2023-11-12 23:53  Moyyer_suiy  阅读(18)  评论(1编辑  收藏  举报