The solution of「P17035 联合挖掘」

\(\textup{P17035 [NWERC 2020] 联合挖掘}\)

\(\textup{Link}\) | 洛谷文章 | \(\textup{Tag: Adhoc}\)

本来是绿,注意到是 noip 模拟赛 T1 一车没切之后丝滑地蓝了。

你问我啊,哦,我并没有看这个题,因为 \(\textup{T0}\) 做了 4h。


\(\textup{Description}\)

给定无向简单图,求构造方案使其被分为一条路径及两个集合,满足两个集合没有连边且大小相同。


\(\textup{Solution}\)

考虑两个集合没有连边这个条件,由于是简单图,发现进行 dfs 的时候,可以把点分为三类:

  • 没遍历过的;
  • 走过已经从栈弹出的;
  • 正在栈里的。

第一类与第二类固然不直接相连,而正在栈里的也必定是一条路径。

于是我们使得第一二类大小相同即可。

最初第一类是全集,而随着遍历,第一类会减小从而比第二类小。

又因为每一次弹栈进栈只会让集合大小变换 1,则一定会有时刻它们相等,找到并输出就做完了。

实现考虑直接用 set 模拟,时间上是 \(O((N + M)\log N)\) 的,应该没有写错欢迎打脸。


\(\textup{Code}\)

\(\textup{Submission.}\)

#include<bits/stdc++.h>
using namespace std;
#define int long long
/*
9.15
https://www.luogu.com.cn/problem/P17035
https://www.luogu.com.cn/article/i5vrg12y
*/
const int MAXN = 2e5 + 10;
const int inf = 0x3f3f3f3f3f3f3f3f;
const int MOD = 1e9 + 7;
int N, M;
struct star{
    int nxt, to;
}edge[MAXN << 1];
int head[MAXN], cnt;
void add( int u, int v ){
    edge[++ cnt] = { head[u], v };
    head[u] = cnt;
}

set<int> a, b;
vector<int> c;

void write(){//输出答案
    cout << c.size() << " " << b.size() << endl;
    for( int j : c ) cout << j << " "; cout << "\n";
    for( int j : a ) cout << j << " "; cout << "\n";
    for( int j : b ) cout << j << " "; exit( 0 );
}

void dfs( int u ){
    for( int i = head[u]; i; i = edge[i].nxt ){
        int v = edge[i].to;
        if( !a.count( v ) ) continue;
        a.erase( a.find( v ) ), c.push_back( v );//入栈
        if( a.size() == b.size() ) write();
        dfs( v );
        c.pop_back(), b.insert( v );//出栈
        if( a.size() == b.size() ) write();
    }
}
void slove(){
    cin >> N >> M;
    for( int i = 2; i <= N; i ++ ) a.insert( i );
    for( int i = 1; i <= M; i ++ ){                         
        int u, v;
        cin >> u >> v;
        add( u, v ), add( v, u );
    }
    c.push_back( 1 );
    if( a.size() == b.size() ) write();//判没有边的情况,比如第一个点就是 1 0
    dfs( 1 );
}

signed main(){
    ios::sync_with_stdio( 0 );
    cin.tie( 0 ), cout.tie( 0 );
    // freopen( "find.in", "r", stdin );
    // freopen( "find.out", "w", stdout );
    int T = 1;
    // cin >> T;
    while( T -- )
        slove();
    return 0;
}

\(\textup{Last}\)

如果您有所疑惑或我有所错漏,请您在评论区指出或找我,我会一定解答并且修改本题解。

如果您觉得本文写的还不错,那可以留个赞吗?

谢谢你看到这里~

posted @ 2026-09-16 21:24  Fαll  阅读(6)  评论(0)    收藏  举报