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}\)
#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}\)
如果您有所疑惑或我有所错漏,请您在评论区指出或找我,我会一定解答并且修改本题解。
如果您觉得本文写的还不错,那可以留个赞吗?
谢谢你看到这里~

浙公网安备 33010602011771号