题目链接

题解

对于维护「某些元素之间必须贴贴相邻」这样的问题,可以考虑使用一种叫做 PQTree 的结构来维护。

PQTree 上的点有三种类型:

  • 叶子结点,表示一个元素
  • P 类节点,表示这个节点的儿子的顺序是可以任意排布的
  • Q 类节点,表示这个节点的儿子的顺序必须是当前给出的顺序或者逆序

现在我们考虑如何加入一个限制。设这一次限制是要求 $S$ 内的元素贴贴,那么我们把 $S$ 内对应的叶子都标记成黑色,将其他叶子标记成白色。对于非叶结点,如果它的子树内只有一种颜色的节点,那么它的颜色和这种颜色相同;否则认为这个点的颜色是灰色

我们先求出每个点的颜色,然后考虑如何去 fix 这个限制。假设我们现在在考虑 $v$ 的子树,我们分类讨论一下:

  • 如果 $v$ 不是灰色节点,那么下面的限制都已经满足了,直接返回
  • 如果 $v$ 是灰色节点:

    • 如果 $v$ 是 P 类节点:设 $v$ 的三种颜色的儿子的集合分别是 $v_B,v_W,v_G$(Black,White,Gray)

      • 如果 $|v_G| = 1$ 且 $|v_B| = 0$:继续去 fix $v_G$ 内的元素,然后直接返回即可。
      • 如果 $|v_G| > 2$,显然出现了矛盾,无解。
      • 其余情况:考虑我们先清空 $v$ 的儿子集合,然后先往 $son_v$ 中加入 $v_W$ 中的所有节点,然后新建一个 Q 类节点 $n_1$,往 $son_v$ 中加入 $n_1$。然后先加入 $v_G$ 到 $son_{n_1}$,然后在 $son_{n_1}$ 中间插入一个新建的 P 类节点 $n_2$,然后令 $son_{n_2} = v_B$ 即可。我们发现这里需要限制这两个灰色节点白色全在左侧,黑色全在右侧(或相反),我们假设现在有一个分裂函数,可以把这个子树的点分裂成黑白部分,并同时保留所有可能,分裂成的子树的节点。我们只需要将 $v_G$ 中的点换成对应的 vector 就好了。
    • 如果 $v$ 是 Q 类节点:

      • 找到最左边和最右边的非白色节点位置 $l,r$,位置指的是 $son_v$ 的相对位置编号。
      • 如果 $[l+1,r-1]$ 内有非黑色节点,无解。
      • 如果没有黑色节点,只有一个灰色节点,递归 fix 这个灰色节点然后返回。
      • 否则只需要将 $l,r$ 位置的节点看情况分裂(只有灰色需要分裂)。

所以现在重点是如何实现这个分裂函数 $v$,WLOG,我们想将 $v$ 分裂成左边全是白色,右边全是黑色的森林。并且我们不能丢失所有可能的顺序关系(所以直接拆开是不可以的),根据上面过程的启发可以得到类似的过程:

  • 如果 $v$ 不是灰色节点,直接返回当前子树。
  • 如果 $v$ 是灰色节点:

    • 如果 $v$ 是 P 类节点:设 $v$ 的三种颜色的儿子的集合分别是 $v_B,v_W,v_G$(Black,White,Gray)

      • 如果 $|v_G| > 1$,显然无解。
      • 否则我们一定是形如,左边是一堆没有顺序关系限制的 $v_W$ 内的点,中间可能是一个 $v_G$,然后右边是没有顺序关系限制的 $v_B$ 中的点。所以我们先新建两个 P 类节点 $n_1,n_2$,$son_{n_1} = v_W,son_{n_2} = v_B$,然后再递归处理 $v_G$ 的分裂情况(如果有的话),最后按照顺序拼起来就好了。最后不要忘记删除节点 $v$。
    • 如果 $v$ 是 Q 类节点:

      • 如果不存在一种顺序(正序和反序),满足排布形式形如白-灰-黑,显然无解。
      • 如果灰色点数量 $>1$,显然无解。
      • 否则我们只需要递归分裂灰色节点即可。
      • 最后不要忘记删除 $v$ 点

分析一下复杂度:假设有 $n$ 个元素,$m$ 次操作。看起来我们每次需要新建很多节点,复杂度会自闭。但是注意到我们可以缩起来所有的链(很多关于树的复杂度用缩小度点和链,环很好用),这样由于叶子永远只有 $n$ 个,那么非叶子节点数量也是 $O(n)$ 的(实际上会有 $n-1$ 个),复杂度就是 $O(nm)$。

具体实现缩链的时候,我们用一个 dfs,每次对于 $v$ 的所有儿子,都一直往下跳到第一个不是链的位置,并将他们全都缩起来。这样可能会处理不好根连出的一条链,但是可以特判(虽然我的代码没判)

代码

#include <bits/stdc++.h>

#define fi first
#define se second
#define DB double
#define U unsigned
#define P std::pair
#define LL long long
#define LD long double
#define pb emplace_back
#define MP std::make_pair
#define SZ(x) ((int)x.size())
#define all(x) x.begin(),x.end()
#define CLR(i,a) memset(i,a,sizeof(i))
#define FOR(i,a,b) for(int i = a;i <= b;++i)
#define ROF(i,a,b) for(int i = a;i >= b;--i)
#define DEBUG(x) std::cerr << #x << '=' << x << std::endl

const int MAXN = 1e5 + 5;

int typ[MAXN],col[MAXN];
std::vector<int> G[MAXN];
int bin[MAXN],tp,tot;
int n,m;

inline void run(){
    puts("-1");
    exit(0);
}

inline int New(int o){
    int v = tp ? bin[tp--] : ++tot;
    typ[v] = o;return v;
}

inline void del(int v){
    G[v].clear();bin[++tp] = v;
}

inline bool chk(std::vector<int> &vec){
    int ps = -1;
    FOR(i,0,SZ(vec)-1) if(col[vec[i]] == 2){
        if(ps != -1) return 0;
        ps = i;
    }
    if(ps == -1){
        FOR(i,0,SZ(vec)-1) if(col[vec[i]]){ps = i;break;}
    }
    FOR(i,0,ps-1) if(col[vec[i]] != 0) return 0;
    FOR(i,ps+1,SZ(vec)-1) if(col[vec[i]] != 1) return 0;
    return 1;
}

inline std::vector<int> split(int v){// 左0右1
    if(col[v] <= 1) return {v};
    if(typ[v]){// Q
        if(!chk(G[v])) std::reverse(all(G[v]));
        if(!chk(G[v])) run();
        std::vector<int> res;
        for(auto x:G[v]){
            if(col[x] != 2) res.pb(x);
            else{
                std::vector<int> tmp = split(x);
                res.insert(res.end(),all(tmp));
            }
        }
        del(v);
        return res;
    }
    else{// P
        std::vector<int> son[3];
        for(auto x:G[v]) son[col[x]].pb(x);
        if(SZ(son[2]) > 1) run();
        std::vector<int> res;
        if(SZ(son[0])){
            int n0 = New(0);G[n0] = son[0];
            res.pb(n0);
        }
        if(SZ(son[2])){
            std::vector<int> tmp = split(son[2][0]);
            res.insert(res.end(),all(tmp));
        }
        if(SZ(son[1])){
            int n1 = New(0);G[n1] = son[1];
            res.pb(n1);
        }
        del(v);
        return res;
    }
}

inline void work(int v){
    if(col[v] <= 1) return;
    if(typ[v]){// Q
        int l = 1e9,r = -1e9;
        FOR(i,0,SZ(G[v])-1) if(col[G[v][i]]) l = std::min(l,i),r = std::max(r,i);
        FOR(i,l+1,r-1) if(col[G[v][i]] != 1) run();
        if(l == r && col[G[v][l]] == 2){
            work(G[v][l]);
            return;
        }
        std::vector<int> son;
        FOR(i,0,l-1) son.pb(G[v][i]);
        if(col[G[v][l]] == 2){
            std::vector<int> tmp = split(G[v][l]);
            son.insert(son.end(),all(tmp));
        }
        else son.pb(G[v][l]);
        FOR(i,l+1,r-1) son.pb(G[v][i]);
        if(l != r){
            if(col[G[v][r]] == 2){
                std::vector<int> tmp = split(G[v][r]);
                std::reverse(all(tmp));
                son.insert(son.end(),all(tmp));
            }
            else son.pb(G[v][r]);
        }
        FOR(i,r+1,SZ(G[v])-1) son.pb(G[v][i]);
        G[v] = son;
    }
    else{// P
        std::vector<int> son[3];
        for(auto x:G[v]) son[col[x]].pb(x);
        if(son[1].empty() && SZ(son[2]) == 1){
            work(son[2][0]);
            return;
        }
        G[v].clear();
        if(SZ(son[2]) > 2) run();
        G[v] = son[0];
        int n1 = New(1);G[v].pb(n1);
        if(SZ(son[2]) >= 1){
            std::vector<int> tmp = split(son[2][0]);
            G[n1].insert(G[n1].end(),all(tmp));
        }

        if(SZ(son[1])){
            int n2 = New(0);
            G[n1].pb(n2);
            G[n2] = son[1];
        }

        if(SZ(son[2]) >= 2){
            std::vector<int> tmp = split(son[2][1]);
            std::reverse(all(tmp));
            G[n1].insert(G[n1].end(),all(tmp));
        }
    }
}

char str[MAXN];

inline void apply(int v){
    if(v >= 2 && v <= m+1){
        col[v] = (str[v-1]=='1');
        return;
    }
    int c0 = 0,c1 = 0;
    for(auto x:G[v]){
        apply(x);
        if(col[x] == 0) c0 = 1;
        else if(col[x] == 1) c1 = 1;
        else c0 = c1 = 1;
    }
    if(c0 && !c1) col[v] = 0;
    else if(!c0 && c1) col[v] = 1;
    else col[v] = 2;
}

inline void gao(int v){
    for(auto &x:G[v]){
        int t = x;
        while(SZ(G[t]) == 1){
            int p = t;
            t = G[t][0];
            del(p);
        }
        x = t;gao(x);
    }
}

inline void dfs(int v){
    if(v >= 2 && v <= m+1){
        printf("%d ",v-1);
        return;
    }
    for(auto x:G[v]) dfs(x);
}

int main(){
    scanf("%d%d",&n,&m);
    int rt = New(0);
    FOR(i,1,m) G[rt].pb(New(0));
    FOR(i,1,n){
        scanf("%s",str+1);
        apply(rt);
        work(rt);
        gao(rt);
    }
    dfs(rt);puts("");
    return 0;
}
Last modification:June 19th, 2021 at 03:45 pm
如果觉得我的文章对你有用,请随意赞赏