字典序相关贪心

发布时间 2023-04-18 23:41:18作者: OIer某罗

AGC010E

【题意】
给定一张 \(n\) 个节点的无向图,点有点权,小 A 要给每条边定向,使得无向图依然是DAG,然后小 B 取出一个拓扑序。小 A 的目标是最大化拓扑序,小 B 的目标是让拓扑序的点权字典序最小。
\(n \le 2000, m \le n(n-1)/2\)

【分析】
这题你要是直接从拓扑序来考虑,想到某一个位置放 \(i\) 行不行,会发现你为了让比它更大的不能和它抢,会需要打标记,这个标记十分具有后效性,不太好维护。

乍一看没啥思路了现在,但是你可以随便推下性质!首先对于每个连通块,它们可以独立考虑,其关系是归并关系。

你考虑连通块有什么好的性质!你会发现,对于 \(m = n - 1\) 的情况,可以定向为一棵树,而 \(m \ge n\) 的情况,可以当作一个外向基环树加上若干条边,然后对于外向基环树改一条边的方向,其他边随便定向下,就可以让某一个点是唯一一个没有入边的点。

image

(如果红色的边很多很杂的话不一定能够在基环树的基础上构造 DAG,但是这确实是一个思路的出发点,而且实际上你从一个点开始 dfs,按照 dfn 定序即可。

总之,我们对于一个连通块是可以做到只留一个点没有入度的,而且是任意点都可以

然后考虑一定选这个点,将其所有边取外向(删掉)。接下来怎么办。分裂成了若干个连通块,但是你现在想想后效性是什么。其实就是只能选择和这个点相邻的点。然后完美递归到了子问题。

然后这个实现有很大说法。其实相当于一个 pq,往里塞所有连通块的最小点,每次取最大的点,删掉,然后将其所有相邻连通块放进去。这个过程相当于一个 bfs,但是要维护连通块信息。

但是这样做时间复杂度是 \(O(n^3)\) 的,不能通过。考虑优化,主要问题是边数很大,但是你分析一下单个连通块的发现只是一个 dfs,那么如果把这个 dfs 树建出来那么在这些森林里跑 bfs 得到的东西一样,但是复杂度 \(O(n^2)\)

#include<bits/stdc++.h>
using namespace std;
#define int long long
//use ll instead of int.
#define f(i, a, b) for(int i = (a); i <= (b); i++)
#define cl(i, n) i.clear(),i.resize(n);
#define endl '\n'
typedef long long ll;
typedef unsigned long long ull;
typedef pair<int, int> pii;
typedef pair<ll, ll> pll;
const int inf = 1e9;
//#define cerr if(false)cerr
//#define freopen if(false)freopen
#define watch(x) cerr  << (#x) << ' '<<'i'<<'s'<<' ' << x << endl
void pofe(int number, int bitnum) {
    string s; f(i, 0, bitnum) {s += char(number & 1) + '0'; number >>= 1; } 
    reverse(s.begin(), s.end()); cerr << s << endl; 
    return;
}
template <typename TYP> void cmax(TYP &x, TYP y) {if(x < y) x = y;}
template <typename TYP> void cmin(TYP &x, TYP y) {if(x > y) x = y;}
//调不出来给我对拍!
//use std::array.
int g[2020][2020];time_t alltime = 0;
int n; int a[2020];vector<int>ans;
priority_queue<int,vector<int>,less<int>> q; int col[2020]; bool vis[2020];
int cnt=0;int fa[2020];
vector<int> t[2020];
void dfs(int x, int c) {
    // cnt++;
    col[x] = c;
    f(i,1,n) if(g[x][i] && !col[i]) {
        t[x].push_back(i); fa[i]=x;
        col[i] = c; dfs(i, c);
    }
}
void bfs() {
    f(i,1,n)if(fa[i]==0){q.push(i);}
    while(!q.empty()) {
        int now = q.top(); q.pop(); ans.push_back(now);
        for(int i : t[now]) q.push(i);
    }
}
bool cmp(int x,int y){return a[x]<a[y];}
signed main() {
    ios::sync_with_stdio(0);
    cin.tie(NULL);
    cout.tie(NULL);
    //freopen();
    //freopen();
    //time_t start = clock();
    //think twice,code once.
    //think once,debug forever.
    cin >> n;
    f(i,1,n)cin>>a[i];
    sort(a+1,a+n+1);
    f(i,1,n)f(j,1,n){
        if(__gcd(a[i],a[j]) != 1) {
            g[i][j]=g[j][i]=1;
        }
    }
    f(i,1,n){
        if(!col[i]) dfs(i, i); 
    }
    bfs();
    for(int i:ans)cout<<a[i]<<" ";
    cout<<endl;
    //time_t finish = clock();
   // cout << "time used:" << alltime * 1.0 / CLOCKS_PER_SEC <<"s"<< endl;
    return 0;
}
/*
2023/4/18
start thinking at 16:25


start coding at h:mm
finish debugging at 22:54
*/