The 2016 ACM-ICPC Asia Dalian Regional Contest HDU 5976 Detachment

题意:T组样例,每组给定一个长度为n的线段,要求把线段拆分为不同的线段n=x1+x2+..+xn,使得x1*x2*x3*..*xn最大

思路:根据均值不等式可以知道x1=x2=..=xn可以使得结果最大,但是线段之间不能相等,所以最终的到的结果线段之间一定是趋近于相等的,但是不知道最终是怎么取值,于是打表找规律

打表代码

int q[N];
int ma;
vector<int>v;
void dfs(int u,int idx,int sum)
{
	if(sum>n)return;
	if(sum==n)
	{
		int now=1;
		_for(i,idx)now*=q[i];
		if(now>ma)
		{
			ma=now;
			v.clear();
			_for(i,idx)v.pb(q[i]);
		}
	}
	for(int i=u+1;i<=n;i++)
	{
		q[idx]=i;
		dfs(i,idx+1,sum+i);
	}
}
void solve()
{
	while(cin>>n)
	{
		ma=0;
		dfs(0,0,0);
		cout<<"线段";for(auto i:v)cout<<i<<" ";cout<<endl;
	}
	return ;
}

可以发现线段个数为3,4,5的第一个分别是2,3,4;2,3,4,5;2,3,4,5,6的等差数列,而线段个数为3,4,5的第二,三,四个分别是前一个的最后一项+1,倒数第二项+1,倒数第三项+1,到第一项+1之后再循环往复,于是可以二分找到线段的个数,然后得到当前线段长度线段个数为x个的第一个数列的差d,然后x/d就是按照前后项+1的规律每一项增加了多少组,x%d就是最后后面x%d个还额外加了1。

由于这道题目遍历整个数列也会超时,于是先预处理前100000个数的阶乘和阶乘逆元,然后每次查询就可以在O(log(n))的时间复杂度计算出线段个数,然后O(1)计算出最大答案

最终代码:

#include <map>
#include <queue>
#include <deque>
#include <cmath>
#include <vector>
#include <cstring>
#include <iostream>
#include <algorithm>
#include <unordered_map>
using namespace std;
#define fi first
#define se second
#define pb push_back
#define pp pop_back()
#define int long long
#define laile cout<<"laile"<<endl
#define lowbit(x) ((x)&(-x))
#define double long double
#define sf(x) scanf("%lld",&x)
#define sff(x,y) scanf("%lld %lld",&x,&y)
#define _for(i,n) for(int i=0;i<(n);++i)
#define _rep(i,a,b) for(int i=(a);i<=(b);++i)
#define all(x) (x).begin(), (x).end()
#define IOS ios::sync_with_stdio(false);cin.tie(0);cout.tie(0)
typedef unsigned long long ULL;
typedef pair<int,int>PII;
const int N=1e6+10,INF=4e18,P=1e9+7;
int n,m;
int res[N];
bool check(int x)
{
	return (2+x)*(x-1)/2<=n;
}
int qmi(int a,int b,int P)
{
	int res=1;
	while(b)
	{
		if(b&1)res=res*a%P;
		a=a*a%P;
		b>>=1;
	}
	return res;
}
int A[N],FA[N];
void init()
{
	A[1]=1;
	FA[1]=qmi(1ll,P-2,P);
	for(int i=2;i<50000;i++)
	{
		A[i]=A[i-1]*i%P;
		FA[i]=qmi(A[i],P-2,P);
	}
	return;
}
void solve()
{
	sf(n);
	if(n==1)
	{
		puts("1");
		return;
	}
	int l=2,r=1e5;
	while(l+1<r)
	{
		int mid=l+r>>1;
		check(mid)?l=mid:r=mid;
	}
	int len=l-1;
	int now=(2+l)*(l-1)/2;
	int cha=(n-now)/len;
	int yu=(n-now)%len;
	int ans=A[len-yu+1+cha]*FA[cha+1]%P*A[len+2+cha]%P*FA[len-yu+2+cha]%P;
	printf("%lld\n",ans);
	return ;
}
signed main()
{
	init();
	int T=1;
	sf(T);
	while(T--)
		solve();
	return 0;
}

  • 9
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值