​传送门​

规定字典序第 k k k小,而且左上角到右上角的任意一条路径都是合法的括号序列

其中 ‘(’ 的字典序小于 ‘)’。

显然满足任意路径都是合法的括号序列的方案需要满足一些条件

我们知道,从左上角随意走 x x x步,到达的点 ( i , j ) (i,j) (i,j)一定满足 i + j = = x + 2 i+j==x+2 i+j==x+2

也就是说,把座标满足 i + j = = x i+j==x i+j==x的方格看成一个整体,也就是把整个放个看成 n + m − 1 n+m-1 n+m−1条对角线

只要对角线上的格子填的括号都相等,那么任意路径都是相等的括号序列,这样是可以的

那么对角线上的括号一定要相等吗?是的

假如把任意一个括号改变,会影响经过这个点的所有路径,然后又会去影响其他点改变,又影响其他路径…

其实就是个规律,证明我也不太会。


那么就按照价值从小到大填数就好了,如何判断当前位置是填 ( ( (还是 ) ) )呢?

因为是求第 k k k小的字典序,所以我们现在这里填个字典序小的 ( ( (

如果这样 d p dp dp出来的方案数大于等于 k k k,那么必须填左括号

因为假如填右括号,那么其余方案只要在这里填左括号,字典序就超过k个更小了

如果这样 d p dp dp的方案小于 k k k,就换成右括号,因为填左括号的话方案数已经不够第 k k k小了

方案数太大,处理一下即可

#include <bits/stdc++.h> 
using namespace std;
#define int long long
const int maxn = 4e4+10;
const int mod = 1e9+10;
int n,m,k,top,vis[maxn],f[409][409];
struct node
{
int x,y,w;
}v[maxn];
bool com(node a,node b){ return a.w<b.w; }
signed main()
{
cin >> n >> m >> k;
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++)
{
scanf("%lld",&v[top].w);
v[top].x = i, v[top].y = j; top++;
}
sort( v,v+top,com );
for(int i=0;i<top;i++)
{
int len = v[i].x+v[i].y;
if( vis[len] ) continue;
vis[len] = -1;//先填左括号
memset( f,0,sizeof f);
f[0][0] = 1;
for(int j=1;j<=n+m-1;j++)
{
if( !vis[j+1] )//没有被填过数字
{
for(int q=0;q<=j;q++)
{
if( q ) f[j][q] = f[j-1][q-1];
f[j][q] = min( mod, f[j][q]+f[j-1][q+1] );
}
}
else if( vis[j+1]==1 )//右括号
{
for(int q=0;q<=j;q++)
f[j][q] = f[j-1][q+1];
}
else
{
for(int q=1;q<=j;q++)
f[j][q] = f[j-1][q-1];
}
}
if( f[n+m-1][0]<=k ) k-=f[n+m-1][0],vis[len] = 1;//换成右括号
}
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++)
{
if( vis[i+j]==1 ) cout << ")";
else cout << "(";
if( j==m ) puts("");
}
return 0;
}