题意:定义数组的分数是sigma C[i],其中C[i]是第i个数左边最大的递增子序列和第i个数右边最大的递减子序列的长度和。给定一个集合,求这个集合的全排列中最大的分数,并求出得到这个最大分数的方法种数。
思路:假设说有一个集合中,数字t的个数是最多的,有m个。那么分数最高的方法就是将这个t充分利用。根据规则,最好就是比t大的都在t的一边,比t小的都在t的一边。这样就分析得到这个数组是一个有序的数组时分数最高。所以,只需要将数组排序就可以了。
对于方法种数。通过举例子可以得到,当这个数组最大的一个数左边是递增的,右边是递减的时候,也可以得到最大的分数。所以方法种数就是这个数组先递增后递减的排列的方法数。将最小的数字固定,把较大的数字往里面插入就好了。
代码如下:
#include<iostream>
#include<cstring>
#include<vector>
#include<algorithm>
#include<cstdio>
#include<map>
#include<cmath>
#include<assert.h>
using namespace std;
typedef long long ll;
const ll mod = 1000000007;
struct Per
{
int v,f;
bool operator < ( const Per &a) const {
return v<a.v;
}
};
Per per[100005];
unsigned long long cnt[100005];
int main()
{
// freopen("data.txt","r",stdin);
int T;
scanf("%d",&T);
int kase=0;
while(T--){
printf("Case %d: ",++kase);
int n;
scanf("%d",&n);
for(int i=0;i<n;++i){
scanf("%d%d",&per[i].v,&per[i].f);
}
sort(per,per+n);
cnt[0]=per[0].f;
for(int i=1;i<n;++i){
cnt[i]=cnt[i-1]+per[i].f;
}
unsigned long long ans=0;
for(int i=0;i<n;++i){
ans=ans+cnt[i]*per[i].f;
}
ll t = 1;
for(int i=0;i<n-1;++i){
t=t*(per[i].f+1)%mod;
}
cout<<ans<<' '<<t<<endl;
}
}