Codeforces 2144F Bracket Groups 题解 [ 紫 ] [ AC 自动机 ] [ DP ] [ 构造 ]

Bracket Groups:赛时猜出来用 ACAM,结果没猜到结论,我是糖比。

首先判掉一些 corner,如果出现了 \(\texttt{()}\) 为单个字符串,则一定无解。

发现后面不太好做,所以可以套路地猜一猜答案上界,发现最多只需要分成两组。具体地,考虑往极端情况构造,弄出下面两种括号串:

  • \(\texttt{((((((} \cdots\texttt{))))))}\)
  • \(\texttt{()()()} \cdots\texttt{()()()}\)

考虑证明,如果 \(s\) 不是这两个串中任意一个串的子串,则放哪一组都可以。如果 \(s\) 为第一个串的子串,那么 \(s\)一定含有两个相同且相邻的字符(一种 corner 是 \(\texttt{)(}\) 的情况,此时也会放到第一组),而这是不可能出现在第二个串中的。如果 \(s\) 为第一个串的子串则也是同理的。

一种判断是否为第一个串的子串的 trick:括号串转 \(1, -1\) 的前缀和,然后判断前缀和之极差是否小于 \(2\)

剩下的只需要考虑能否被一个括号串全部包含即可。这是个经典的 ACAM 上 DP 的问题。\(dp_{i, j, k}\) 表示考虑到第 \(i\) 个字符,括号串前缀和为 \(j\),ACAM 上在节点 \(k\) 处是否可行,然后转移的时候记录一下前驱即可。最后反过来搜一遍构造出方案。

时间复杂度 \(O(nk^3)\)

#include <bits/stdc++.h>
#define fi first
#define se second
#define eb(x) emplace_back(x)
#define pb(x) push_back(x)
#define lc(x) (tr[x].ls)
#define rc(x) (tr[x].rs)
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
typedef long double ldb;
using pi = pair<int, int>;
const int N = 55, V = 3005;
int n, m, ch[V][2], idx, tag[V], ne[V];
int s[N][N], slen[N];
char ts[N];
void insert(int id)
{
    int p = 0;
    for(int i = 1; i <= slen[id]; i++)
    {
        if(ch[p][s[id][i]] == 0) ch[p][s[id][i]] = ++idx;
        p = ch[p][s[id][i]];
    }
    tag[p] = 1;
}
vector<int> g[V];
void dfs(int u)
{
    for(auto v : g[u])
    {
        tag[v] |= tag[u];
        dfs(v);
    }
}
void build()
{
    queue<int> q;
    for(int i = 0; i < 2; i++)
    {
        if(ch[0][i]) q.push(ch[0][i]);
    }
    while(!q.empty())
    {
        int u = q.front();
        q.pop();
        for(int i = 0; i < 2; i++)
        {
            int v = ch[u][i];
            if(v) { ne[v] = ch[ne[u]][i]; q.push(v); }
            else ch[u][i] = ch[ne[u]][i];
        }
    }
    for(int i = 1; i <= idx; i++) g[ne[i]].push_back(i);
    dfs(0);
}
bitset<V> dp[N][N];
tuple<int, int, int> pre[N][N][V];
char ans[N];
vector<int> ans2, ans3;
void construct(int p)
{
    int i = m, j = 0;
    while(i)
    {
        tuple<int, int, int> tmp = pre[i][j][p];
        ans[i] = (get<1>(tmp) - j > 0 ? ')' : '(');
        j = get<1>(tmp);
        p = get<2>(tmp);
        i--;
    }
    cout << 1 << "\n";
    cout << ans + 1 << "\n";
    cout << n << "\n";
    for(int i = 1; i <= n; i++) cout << i << " ";
}
int main()
{
    //freopen("sample.in", "r", stdin);
    //freopen("sample.out", "w", stdout);
    ios::sync_with_stdio(0);
    cin.tie(0);
    cout.tie(0);
    cin >> n >> m;
    for(int i = 1; i <= n; i++)
    {
        cin >> ts + 1;
        slen[i] = strlen(ts + 1);
        if(slen[i] == 2 && ts[1] == '(' && ts[2] == ')')
        {
            cout << "-1";
            return 0;
        }
        for(int j = 1; j <= slen[i]; j++)
            s[i][j] = (ts[j] == '(' ? 1 : 0);
        insert(i);
    }
    build();
    dp[0][0][0] = 1;
    for(int i = 0; i < m; i++)
        for(int j = 0; j <= m; j++)
            for(int k = 0; k <= idx; k++)
            {
                if(dp[i][j][k] == 0) continue;
                if(tag[ch[k][0]] == 0 && j - 1 >= 0)
                {
                    dp[i + 1][j - 1][ch[k][0]] = 1;
                    pre[i + 1][j - 1][ch[k][0]] = {i, j, k};
                }
                if(tag[ch[k][1]] == 0)
                {
                    dp[i + 1][j + 1][ch[k][1]] = 1;
                    pre[i + 1][j + 1][ch[k][1]] = {i, j, k};
                }                
            }
    for(int i = 0; i <= idx; i++)
    {
        if(dp[m][0][i])
        {
            construct(i);
            return 0;
        }
    }
    cout << "2\n";
    for(int i = 1; i <= m; i++) 
    {
        if(i & 1) cout << "(";
        else cout << ")";
    }
    cout << "\n";
    for(int i = 1; i <= n; i++)
    {
        int mn = 0, mx = 0, now = 0;
        for(int j = 1; j <= slen[i]; j++)
        {
            if(s[i][j] == 0) now--;
            else now++;
            mn = min(mn, now);
            mx = max(mx, now);
        }
        if(mx - mn >= 2) ans2.push_back(i);
        else ans3.push_back(i);
    }
    cout << ans2.size() << "\n";
    for(auto itm : ans2) cout << itm << " ";
    cout << "\n";
    for(int i = 1; i <= m / 2; i++) 
        cout << "(";
    for(int i = 1; i <= m / 2; i++) 
        cout << ")";
    cout << "\n";
    cout << ans3.size() << "\n";
    for(auto itm : ans3) cout << itm << " ";
    cout << "\n";    
    return 0;
}
posted @ 2025-09-18 23:39  KS_Fszha  阅读(9)  评论(0)    收藏  举报