H. Zé Coquinho, the sculptor 计数 括号题 2010 USP Try-outs

题意:输入n,K,求第K大的非法括号序列,若没有第K大,输出-1。题目保证运算的数据都在long long范围内。

解法:

1. 如果n为奇数,那么所有的序列都是非法的,令右括号为二进制1,左括号为二进制0,从高位往低位决定每一位是左括号还是右括号。其实输出的就是K的二进制表示。

2. 如果n为偶数,同上一题,还是从高位往低位决定前缀是什么,从而确定每一位是左括号还是右括号,只是这次在求一个前缀的大小时,需要先加上所有可能,再减去合法的括号序列个数。定义函数get(n,i, j, k),n为括号序列总长度,i为当前前缀的左括号个数,j为当前前缀的右括号个数,k为当前前缀是否合法。get返回当前前缀下,非法括号序列的个数。

get的实现:若当前前缀已经非法,直接返回所有可能的括号序列,即2^(n-i-j)。若当前前缀合法,用折线法求出当前前缀下,合法括号个数T=C(n-i-j,n/2-i)-C(n-i-j,n/2-i-1),组合数边界需要注意一下。则非法个数即为2^(n-i-j)-T。

3. 没有第K大的判断方式:若生成完括号序列之后,K还没有被减为0,便视为非法情况。

#include <bits/stdc++.h>
using namespace std;
const int maxn=55;
int q,n;
long long K,C[maxn][maxn];
inline void init() {
    for (int i=0;i<maxn;++i)
        C[i][0]=1;
    for (int i=1;i<maxn;++i)
        for (int j=1;j<=i;++j)
            C[i][j]=C[i-1][j]+C[i-1][j-1];
}
inline void print1() {
    --K;
    string s;
    for (int i=n-1;i>=0;--i)
        if (K>=(1LL<<i)) {
            K-=(1LL<<i);
            s+=')';
        } else
            s+='(';
    if (K)
        cout<<-1<<endl;
    else
        cout<<s<<endl;
}
inline long long get(int l,int r,int f) {
    long long ret=1LL<<(n-l-r);
    if (f)
        return ret;
    if (n/2-l>=0)
        ret-=C[n-l-r][n/2-l];
    if (n/2-l>0)
        ret+=C[n-l-r][n/2-l-1];
    return ret;
}
inline void print2() {
    string s;
    int det=0,f=0,l=0,r=0;
    for (int i=0;i<n;++i) {
        long long temp=get(l+1,r,f);
        if (temp>=K) {
            ++l;
            ++det;
            s+='(';
        } else {
            ++r;
            K-=temp;
            s+=')';
            --det;
            if (det<0)
                f=1;
        }
    }
    --K;
    if (K)
        cout<<-1<<endl;
    else
        cout<<s<<endl;
}
int main()
{
    init();
    scanf("%d",&q);
    while (q--) {
        scanf("%d%I64d",&n,&K);
        if (n&1)
            print1();
        else
            print2();
    }
    return 0;
}


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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值