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 ≤ 18,1 ≤ ai ≤ 109,1 ≤ 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。
将式子拆一拆,发现答案即为(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;
}