gym100801I Insider's Information

題意

要求構造大小爲 \(n\) 的排列,要求至少 \(\lceil \frac{m}{2} \rceil\) 條限制:第 \(i\) 條限制 \((a_i,b_i,c_i)\) 形如排列中 \(b_i\) 的位置要在 \(a_i\)\(c_i\) 之間。

題解

神仙題。

這個題一般確定排列的方法都不能用了,我們考慮這個在兩個數中間的限制:考慮用按照順序每次將一個數放到前面或者後面的方式。

因爲保證有解,所以肯定能搞出來一個答案,也就肯定有一種放置順序。我們假設我們知道了放置順序,考慮如何去構造出答案,分類討論一下:

假設一個限制中第一個被放入的數是 \(a_i\),那麼如果下一個數是 \(b_i\),就要和 \(a_i\) 放在同側,否則就要放在不同側。

所以我們加入一個點的時候,算出它放在左邊和右邊分別能滿足多少限制,選擇最大的那個即可。這樣相當於將所有限制分成若干組,每一組至少取了一半,所以滿足題目限制。

現在問題在於如何找到這個放置順序:發現如果在三元組內是 \(b_i\) 先出現,那麼怎麼放置都不滿足條件,所以我們要求出的序列形如要求 \(a_i\)\(c_i\) 出現在 \(b_i\) 之前,用類似拓撲排序的方法即可。

#include <bits/stdc++.h>

#define fi first
#define se second
#define db double
#define U unsigned
#define P std::pair<int,int>
#define LL long long
#define pb push_back
#define MP std::make_pair
#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 = 2e5 + 5;
int n,m,a[MAXN],b[MAXN],c[MAXN];
std::vector<int> G[MAXN],lim[MAXN];
int deg[MAXN];
int dfn[MAXN],p[MAXN];
bool zt[MAXN];// left=0 right=1
bool vis[MAXN];

int main(){
#ifndef RainAir
    freopen("insider.in","r",stdin);
    freopen("insider.out","w",stdout);
#endif
    scanf("%d%d",&n,&m);
    FOR(i,1,m){
        scanf("%d%d%d",a+i,b+i,c+i);
        G[a[i]].pb(i);
        G[c[i]].pb(i);
        ++deg[b[i]];
    }
    std::queue<int> q;int ts = 0;
    FOR(i,1,n) if(!deg[i]) q.push(i);
    while(!q.empty()){
        int v = q.front();q.pop();
        dfn[v] = ++ts;
        for(auto x:G[v]){
            if(vis[x]) continue;
            vis[x] = 1;
            if(!--deg[b[x]]){
                q.push(b[x]);
            }
        }
    }
    FOR(i,1,m) assert(!(dfn[b[i]] < dfn[a[i]] && dfn[b[i]] < dfn[c[i]]));
    FOR(i,1,n) p[dfn[i]] = i;
    FOR(i,1,m) if(dfn[a[i]] > dfn[c[i]]) std::swap(a[i],c[i]);
    FOR(i,1,m) lim[b[i]].pb(i),lim[c[i]].pb(i);
    FOR(i,1,n){
        int cnt[2];CLR(cnt,0);
        int v = p[i];
        for(auto x:lim[v]){
            if(v == b[x]){
                if(dfn[c[x]] < dfn[b[x]]) continue;
                cnt[zt[a[x]]]++;
            }
            if(v == c[x]){
                if(dfn[b[x]] < dfn[c[x]]) continue;
                cnt[zt[a[x]]^1]++;
            }
        }
        zt[v] = cnt[0] > cnt[1] ? 0 : 1;
    }
    std::vector<int> v1,v2;
    FOR(i,1,n){
        if(zt[p[i]]) v2.pb(p[i]);
        else v1.pb(p[i]);
    }
    std::reverse(all(v2));
    for(auto x:v1) printf("%d ",x);
    for(auto x:v2) printf("%d ",x);
    puts("");
    return 0;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章