6879. 【2020.11.21提高组模拟】T1 出了个大阴间题(repair)

本文针对一项具体的算法竞赛题目进行解析,介绍了如何通过状态压缩动态规划求解最优排列问题,包括输入输出格式、样例说明及数据约束,并给出了完整的C++代码实现。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Description

 

Input

从文件 repair.in 中读入数据。
第一行输入两个正整数 n, k
第二行输入 n 个正整数 ai

Output

输出到文件 repair.out 中。
共一行输出两个整数,表示最大的 a 与代价总和。

Sample Input

3 1
1 1 2
2 页 共 9 模拟赛
T1 出了个大阴间题(repair

Sample Output

3 12
【样例 1 解释】
共有两个排列符合条件:1, 2, 3 2, 1, 3,合并代价均为 6
【样例 2
见选手目录下的 repair/repair2.in repair/repair2.ans

Data Constraint

对于所有数据,保证 1 n 181 ai 1091 k < 109 + 7
对于第 1 ~ 4 组数据,保证 n 10
对于第 5 ~ 6 组数据,保证 ai n 15
对于第 7 ~ 8 组数据,保证 ai n
对于第 9 ~ 10 组数据,没有特殊限制

Solution

注意到只能是一个一个合并的,因此合并后的值只能是其中一个a或a+1。

同时,每一次合并完后的值也都是集合中的a或a+1。

将式子拆一拆,发现答案即为k*\sum_{p=1}^{sum}\sum_{i=1}^{n-1}new_A[~~A_p[i]~~][~~A_p[i+1]~~]+sum*\sum_{i=1}^{n-2}2^i-1(sum为最后newA最大的排列方案的个数)

其中p是任意一种排列的方案,newA是将Ap [ i ]和Ap [ i+1 ]合并后得到的a,后面的sigma是b的和,为定值。

因此设f[ S ][ 0/1 ]表示当前选择了集合S,当前所得出的newA的最大值、次大值的值,得到这个值的排列个数,以及所有排列的贡献(即每一次合并后newA之和)。

枚举一个 i 转移即可。

时间复杂度O(n*2^n)

Code

#include<cstdio> 
#include<cstring>
#include<algorithm>
#define I int
#define ll long long
#define F(i,a,b) for(I i=a;i<=b;i++)
#define M 1000000007
using namespace std;
I n,k,a[20],x,T;
struct node{I a,s,t;}f[262170][2];
I R(I &x){
	x=0;I w=1;char c=getchar();
	while(c<'0'||c>'9'){if(c=='-') w=-1;c=getchar();}
	while(c>='0'&&c<='9'){x=x*10+c-'0';c=getchar();}
	return x*=w;
}
void update(I S,I o,I p){
	if(!f[S][p].a) return;
	x=max(f[S][p].a,a[o])+(f[S][p].a==a[o]);
	if(x<f[T][0].a){
		if(x>f[T][1].a) f[T][1]=node{x,0,0};
		if(x==f[T][1].a) f[T][1]=node{f[T][1].a,(f[T][1].s+1ll*x*f[S][p].t%M+f[S][p].s)%M,(f[T][1].t+f[S][p].t)%M};
	}
	else{
		if(x>f[T][0].a) f[T][1]=f[T][0],f[T][0]=node{x,0,0};
		if(x==f[T][0].a) f[T][0]=node{f[T][0].a,(f[T][0].s+1ll*x*f[S][p].t%M+f[S][p].s)%M,(f[T][0].t+f[S][p].t)%M};
	}
}
I main(){
	freopen("repair.in","r",stdin);
	freopen("repair.out","w",stdout);
	R(n),R(k);
	F(i,1,n) R(a[i]),f[1<<(i-1)][0]=node{a[i],0,1};
	F(S,1,(1<<n)-1) if(f[S][0].a){
		F(i,1,n) if(!((S>>(i-1))&1)){
			T=S+(1<<(i-1));
			update(S,i,0),update(S,i,1);
		}
	}
	x=T=0;
	F(i,2,n){
		x=1ll*(x+T)%M;
		T=(T*2ll+1)%M;
	}
	T=(1<<n)-1;
	printf("%d %d\n",f[T][0].a,(1ll*f[T][0].s*k%M+1ll*x*f[T][0].t%M)%M);
	return 0;
} 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值