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

来源:互联网 发布:编程语言 知乎 编辑:程序博客网 时间:2024/06/06 07:49

题意:输入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;}


原创粉丝点击