【题目大意】
有N种字符,第i种字符有Ti个。用这些字符组成长度>=L的字符串,求这其中第M的大的串。
【思路】
以样例1为例:
3 37 1 I 1 P 1 C 5我们假定第一位放的字符为P(ASCII码最大),我们使用剩下的字符组成长度>=0(L-1)的字符串,假设组合方式大于等于37(M),那么说明第一位放的就应该是P。如果组合方式小于37,我们就尝试第一位放I(ASCII第二大),去寻找后面的组合方式是否能>= M - 第一位放P时,后面的组合数。这样一直下去,我们就可以把第M大的串找出来。
如果我们能在O(X)的时间复杂度内计算出后面的组合数,解决整个问题的复杂度,最坏为O(X * L * N)。我的算法中,X == (∑Ti)^2,下面具体介绍怎么去算这个组合数。
我们用dp[i][j] 表示已经使用了前i种字符,总共使用了j个字符,组成不同串的方法数。当我们想加入第i+1种字符的时候,可以枚举第i+1种字符具体用多少个,然后把这些字符插到前面的字符空隙中。简单地说,如果当前状态为dp[i][j],想插入k个字符到原状态中,可以视为:有k个相同的苹果,放到(j+1)个不同的盘子中,有多少种放法,这个值 = C(j+1+k-1,k),具体怎么证明的,我忘了,现在只知道有这个结论o(╯□╰)o
至此,这题大概就能做了,最后有两个细节问题,1、数据的溢出,2、我们一位一位地构造,什么时候退出构造。问题1的话,随便判断一下上界就行了。问题2的话,我采用的方法是:先判断这位后面是否能加入空串,如果不能,说明这位肯定还得放字符,继续放就行了,如果能放空串,判断后面的组合数是否等于当前剩下的k,如果相等,就退出构造(因为空串的字典序显然是最小的)。
//#pragma comment(linker, "/STACK:102400000,102400000")
#include<cstdio>
#include<cstring>
#include<vector>
#include<queue>
#include<cmath>
#include<cctype>
#include<string>
#include<algorithm>
#include<iostream>
#include<ctime>
#include<map>
#include<set>
using namespace std;
#define MP(x,y) make_pair((x),(y))
#define PB(x) push_back(x)
typedef long long LL;
//typedef unsigned __int64 ULL;
/* ****************** */
const LL INF = 1LL<<55;
const double INFF = 1e100;
const double eps = 1e-8;
const LL mod = 10000000007LL;
const int NN = 1010;
const int MM = 400010;
/* ****************** */
struct node
{
char c;
int x;
bool operator<(const node &tt)const
{
return c < tt.c;
}
}a[40];
LL dp[40][370];
LL sum[40];
LL c[800][800];
void init()
{
int i, j, n = 800;
LL limit = 1000000000000000000LL;
memset(c, 0, sizeof(c));
for(i = 0; i < n; i ++)
c[i][i] = c[i][0] = 1;
for(i = 0; i < n; i ++)
for(j = 1; j < i; j ++)
{
c[i][j] = c[i-1][j] + c[i-1][j-1];
if(c[i][j] > limit)
c[i][j] = limit + 1;
}
}
void INC(LL &a,LL b,LL k)
{
a += b;
if(a > k) a = k + 1;
}
LL solve(int n,int l,LL kk)
{
memset(dp, 0, sizeof(dp));
dp[0][0] = 1;
int i, j, k;
LL temp, ans = 0;
sum[0] = 0;
for(i = 1; i <= n; i ++)
sum[i] = sum[i-1] + a[i].x;
for(i = 0; i < n; i ++)
for(j = 0; j <= sum[i]; j ++)
{
if(dp[i][j]==0)continue;
for(k = 0; k <= a[i+1].x; k ++)
{
if((double)dp[i][j]*c[j+k][k] > kk + 1)
temp = kk + 1;
else
temp = dp[i][j]*c[j+k][k];
temp = min(temp, kk + 1);
INC(dp[i+1][j+k], temp, kk);
}
}
for(i = l; i <= sum[n]; i ++)
{
INC(ans, dp[n][i], kk);
}
return ans;
}
int main()
{
init();
int n, l, i, j;
LL k, temp;
char op[5];
while(cin>>n>>k>>l)
{
for(i = 1; i <= n; i ++)
{
scanf("%s%d", op, &a[i].x);
a[i].c = op[0];
}
sort(a+1,a+1+n);
if(solve(n, l, k) < k)
puts("-1");
else
{
string ans = "";
for(i = 1; ; i ++)
{
if(i-1>=l && solve(n, 0, k)==k)
break;
for(j = n; j >= 1; j --)
{
if(a[j].x == 0)continue;
a[j].x --;
temp = solve(n, max(l-i, 0), k);
if(temp >= k)
break;
a[j].x ++;
k -= temp;
}
ans += a[j].c;
}
cout<<ans<<endl;
}
}
return 0;
}