题解 ABC233F Swap and Sort

给定一个长 \(n(\le 1000)\) 的排列和 \(m\) 种交换方式,构造方案使在 \(5\times 10^5\) 步中使序列有序。

一个显然的结论是构造出这个图之后如果一个数和它应该在的位置在同一个联通块,那么肯定是有解的,因为达到任何顺序都不会超过 \(\frac{n^2}{2}\) 次。

考虑构造出这个图,取它的一个生成树森林,每次对于一个点遍历这棵树,找到应该换到这个点的数的位置,一路和其父亲交换就可以了。

注意要从叶子开始换,不然可能会影响到前面的。

int n, a[N], m, x[M], y[M], vis[N];
bool flag;
std::stack<int> st;
std::vector<std::pair<int, int>> g[N];
std::vector<int> ans, ord;
void dfs(int u, int to) {
    if (flag) return;
    if (a[u] == to) {
        int now = u;
        while (st.size()) {
            ans.push_back(st.top()); 
            int r = x[st.top()] == now ? y[st.top()] : x[st.top()];
            std::swap(a[now], a[r]);
            now = r;
            st.pop();
        }
        flag = 1;
        return;
    }
    vis[u] = 1;
    for (auto c : g[u]) {
        if (vis[c.first]) continue;
        st.push(c.second), dfs(c.first, to);
        if (flag) return;
        if (st.size()) st.pop();
    }
}
void gord(int u) {
    vis[u] = 1, ord.push_back(u);
    for (auto c : g[u])
        if (!vis[c.first]) gord(c.first);
}
void main() {
	std::cin >> n;
	for (int i = 1; i <= n; i++) std::cin >> a[i];
	std::cin >> m;
	for (int i = 1; i <= m; i++) {
	    std::cin >> x[i] >> y[i];
	    g[x[i]].emplace_back(y[i], i), g[y[i]].emplace_back(x[i], i);
	}
	for (int i = 1; i <= n; i++) 
	    if (!vis[i]) gord(i);
	std::reverse(ord.begin(), ord.end());
	for (int i : ord) {
	    for (int j = 1; j <= n; j++) vis[j] = 0;
	    flag = 0;
	    for (int j : ord) {
	        if (j == i) break;
	        vis[j] = 1;
	    }
	    dfs(i, i);
	    if (flag == 0) return std::cout << -1, void();
	}
	for (int i = 1; i <= n; i++) 
	    if (a[i] != i) return std::cout << -1, void();
	std::cout << ans.size() << '\n';
	for (int i : ans) std::cout << i << ' ';
}
posted @ 2021-12-26 07:54  Acfboy  阅读(189)  评论(0编辑  收藏  举报