[luogu p1249] 最大乘积

传送门

最大乘积

题目描述

一个正整数一般可以分为几个互不相同的自然数的和,如 \(3=1+2\)\(4=1+3\)\(5=1+4=2+3\)\(6=1+5=2+4\)

现在你的任务是将指定的正整数 \(n\) 分解成若干个互不相同的自然数的和,且使这些自然数的乘积最大。

输入输出格式

输入格式

只一个正整数 \(n\),(\(3 \leq n \leq 10000\))。

输出格式

第一行是分解方案,相邻的数之间用一个空格分开,并且按由小到大的顺序。 第二行是最大的乘积。

输入输出样例

输入样例 #1

10

输出样例 #1

2 3 5
30

分析

此题的关键是抓住和同近积大的原理。
和同近积大:两个数的和一定,差小,积大。
也就是说,假设\(a \ge b\),那么\((a + 1) \times (b - 1) < a \times b\)

简证:\((a + 1) \times (b - 1) = a \times b + b - a - 1\),而既然\(a \ge b\),那么\(b - a \le 0\)。最后这个算式还有个减一,因此\((a + 1) \times (b - 1) < a \times b\)

我们考虑在两个的和同近积大原理上进行扩展。因为此题要求所有数字互不相同,我们也想要尽可能多的因数,然后他们还越接近越好。于是如此,我们就可以构造一个由2开始的自然数数列,然后一直求和,直到这个和大于了\(n\)

为什么从\(2\)开始?如果从\(1\)开始的话,\(1\)浪费了这个数和的一块,却对整体的积一点用都没用。血亏!

但是此时和是大于\(n\)的,那么假设多出来的是\(k\)吧,那么此时就把值为\(k\)的那项移出去。此时得到的就是正确的数列——

吗?

  • 不,你忘考虑了\(k = 1\)的情况。这种情况下的最优解是去掉开头的2,然后给末尾项加1。
  • 你还忘考虑了\(n = 3,4\)的情况,此时的解就是\(n\)本身。

其实我有一个问题,就是上述解法是我根据不严谨的思路推出来的,我还没有想好一种严格证明的方案,有小伙伴能告诉我吗?我感激不尽。

哦对了,这道题还需要高精度哦,别忘了,\(n \le 10000\),不开高精见祖宗呢。

代码

本代码采用了高精模板。link

/*
 * @Author: crab-in-the-northeast 
 * @Date: 2020-04-02 00:58:54 
 * @Last Modified by: crab-in-the-northeast
 * @Last Modified time: 2020-04-02 01:10:27
 */
#include <iostream>
#include <cstdio>
#include <cstring>
#include <string>
#include <cassert>
#include <vector>

int max(int a,int b) {
	return a > b ? a : b;
}

struct ubigint {
	typedef unsigned long long ull;

	static const int base = 100000000;
	static const int width = 8;
	
	std :: vector<int> s;

	ubigint& clear_zero() {
		while(!s.back() && s.size() > 1) s.pop_back();
		return *this;
	}
	
	ubigint(ull num = 0) {*this = num;}
	ubigint(std :: string str) {*this = str;}

	ubigint& operator = (ull num) {
		s.clear();
		do {
			s.push_back(num % base);
			num /= base;
		}while(num);
		return *this;
	}

	ubigint& operator = (const std :: string& str) {
		s.clear();
		int x;
		int len = (str.length() - 1) / width + 1;
		for(int i = 0; i < len; i++) {
			int endidx = str.length() - i * width;
			int startidx = max(0, endidx - width);
			int x;
			sscanf(str.substr(startidx, endidx - startidx).c_str(), "%d", &x);
			s.push_back(x);
		}
		return (*this).clear_zero();
	}

    bool operator < (const ubigint& b) const {
        if(s.size() != b.s.size()) return s.size() < b.s.size();
        for(int i = s.size() - 1; i >= 0; i--)
            if(s[i] != b.s[i])
                return s[i] < b.s[i];
        return false;
    }
    bool operator > (const ubigint& b) const {return b < *this; }
    bool operator <= (const ubigint& b) const {return !(b < *this);}
    bool operator >= (const ubigint& b) const {return !(*this < b);}
    bool operator == (const ubigint& b) const {return !(b < *this) && !(b > *this);}
    bool operator != (const ubigint& b) const {return b < *this || *this < b;}

    ubigint operator + (const ubigint& b) const {
        ubigint res;
        res.s.clear();
        for(int i = 0, x = 0; x || i < s.size() || i < b.s.size(); i++, x /= base) {
            if(i < s.size()) x += s[i];
            if(i < b.s.size()) x += b.s[i];
            res.s.push_back(x % base);
        }
        return res.clear_zero();
    }

	ubigint operator - (const ubigint& b) const {
		assert(*this >= b);
		ubigint res;
		res.s.clear();
		for(int i = 0, last = 0;last || i < s.size() || i < b.s.size();i++) {
			int x = s[i] + last;
			if(i < b.s.size()) x -= b.s[i];
			if(x < 0) {
				last = -1;
				x += base;
			}else last = 0;
			res.s.push_back(x);
		}
		return res.clear_zero();
	}

	ubigint operator * (const ubigint& b) const {
		std :: vector<ull> tmp(s.size() + b.s.size(),0);
		ubigint res;
		res.s.clear();
		for(int i = 0; i < s.size(); i++)
			for(int j = 0; j < b.s.size(); j++)
				tmp[i + j] += ull(s[i]) * b.s[j];
		
        ull last = 0;
		for(int i = 0; last || i < tmp.size(); i++) {
			ull x = tmp[i] + last;
			res.s.push_back(x % base);
			last = x / base;
		}
		return res.clear_zero();
	}

	int midsearch(const ubigint& b, const ubigint& m) const {
		int l = 0, r = base - 1;
		while(1) {
			int mid = l + r >> 1;
			if(b * mid <= m && b * (mid + 1) > m) return mid;
			if(b * mid <= m) l = mid;
			else r = mid;
		}
	}

	ubigint operator / (const ubigint& b) const {
		assert(b > 0);
		ubigint res = *this, mod;
		for(int i = s.size() - 1; i >= 0; i--) {
			mod = mod * base + s[i];
			res.s[i] = midsearch(b, mod);
			mod -= b * res.s[i];
		}
		return res.clear_zero();
	}

	ubigint operator % (const ubigint& b) const {
		assert(b > 0);
		ubigint res = *this, mod;
		for(int i = s.size() - 1; i >= 0; i--) {
			mod = mod * base + s[i];
			res.s[i] = midsearch(b, mod);
			mod -= b * res.s[i];
		}
		return mod.clear_zero();
	}

	ubigint& operator += (const ubigint& b) {*this = *this + b; return *this;}
	ubigint& operator -= (const ubigint& b) {*this = *this - b; return *this;}
	ubigint& operator *= (const ubigint& b) {*this = *this * b; return *this;}
	ubigint& operator /= (const ubigint& b) {*this = *this / b; return *this;}
	ubigint& operator %= (const ubigint& b) {*this = *this % b; return *this;}
	

	friend std :: istream& operator >> (std :: istream& in, ubigint& x) {
		std :: string str;
		if(!(in >> str)) return in;
		x = str;
		return in;
	}

	friend std :: ostream& operator << (std :: ostream& out, ubigint x) {
		out << x.s.back();
		for(int i = x.s.size() - 2; i >= 0; i--) {
			char buf[20];
			sprintf(buf, "%08d", x.s[i]);
			for(int j = 0; j < strlen(buf); j++)
				out << buf[j];
		}
		return out;
	}
};

int main() {
    int n;
    std :: cin >> n;
	int sum = 0, to;
    if(n <= 4) {
        std :: cout << n << std :: endl << n << std :: endl;
        return 0;
    }
    for(int i = 2; sum <= n; i++) {
        sum += i;
        to = i;
    }
    ubigint ans = 1;
    for(int i = 2; i <= to; i++) {
		if(sum - n == 1)  {
			if(i == 2) continue;
			if(i == to) {
				std :: cout << i + 1 << ' ';
				ans *= (i + 1);
				break;
			}
		}
		if(i != sum - n) {
            std :: cout << i << ' ';
            ans *= i;
        }
    }
    std :: cout << std :: endl << ans << std :: endl;
    return 0;
}

评测结果

  • AC 100:R32389140(本代码虽然AC了数据,但解法有纰漏,没有特判多出来的是\(1\)的情况。)
  • AC 100:R32389231
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值