usaco2016open gold2 closing

21 篇文章 0 订阅
10 篇文章 0 订阅

Gold2

【题目描述】

FJ和他的奶牛们正在计划离开小镇做一次长的旅行,同时FJ想临时地关掉他的农场以节省一些金钱。

 

这个农场一共有被用M条双向道路连接的N个谷仓(1<=N,M<=200000)。为了关闭整个农场,FJ 计划每一次关闭掉一个谷仓。当一个谷仓被关闭了,所有的连接到这个谷仓的道路都会被关闭,而且再也不能够被使用。

 

FJ现在正感兴趣于知道在每一个时间(这里的“时间”指在每一次关闭谷仓之后的时间)时他的农场是否是“全连通的”——也就是说从任意的一个开着的谷仓开始,能够到达另外的一个谷仓。注意自从某一个时间之后,可能整个农场都开始不会是“全连通的”。

 

【输入格式】

输入的第一行是N和M。下面的M行每行都描述了一条连接两个谷仓的双向路径的两个端点(输入的点保证在1...N的范围内),最后的N行是一个1...N的排列,描述每一个谷仓被关闭的顺序。

 

【输出顺序】

输出一共有N行,每行可以是“YES”或者“NO”。第一行表示一开始时整个农场是否是“全连通的”,然后第i+1行表示在第i次的关闭谷仓之后整个农场是否是“全连通的”。

 

【样例输入】

4 3
1 2
2 3
3 4
3
4
1
2

 

 

【样例输出】

YES
NO
YES
YES

================================================================

本题题意和silver3 完全相同,这里不再赘述,只是数据范围从3000 变成了200000 而已.用并查,显然也能轻松解决;不过在图的储存方面,silver3 时我偷懒写了个邻接矩阵,显然这题如果用邻接矩阵会爆空间.因此要换成邻接链表,不过对于我这种不会手写指针和链表的弱渣当然是用 STL 里方便的 vector 代替了...注意下面的代码看上去和 silver3 的版本出入挺大,实际上核心思想是一样的,只是这里实现起来的时候换了另一种方式:每次统计图中有多少个顶点集合,显然只有 1 个集合的情况就是连通图.

#include<fstream>
#include<iostream>
#include<vector>
using namespace std;
const int maxn = 200005;
//ifstream fin("closing.in");
//ofstream fout("closing.out");
int n, m, fa[maxn], a[maxn];
vector<int>adj[maxn];
bool f[maxn], ans[maxn];
int find(int);
int main() {
freopen("closing.in", "r", stdin);
freopen("closing.out", "w", stdout);
//ios::sync_with_stdio(false);
//fin>>n>>m;
scanf("%d%d", &n, &m);

for(int i = 1; i != n + 1; ++i)fa[i] = i;

for(int i = 0; i != m; ++i) {
int u, v;
//fin>>u>>v;
scanf("%d%d", &u, &v);
adj[u].push_back(v);
adj[v].push_back(u);
}

for(int i = 0; i != n; ++i)scanf("%d", &a[i]); //fin>>a[i];

int cnt = 0;

for(int i = n - 1; i != -1; --i) {
++cnt;

for(int j = 0; j != adj[a[i]].size(); ++j) {
int node = adj[a[i]][j];

if(f[node] && find(node) != find(a[i])) {
--cnt;
fa[find(a[i])] = find(node);
}
}

ans[i] = cnt == 1;
f[a[i]] = true;
}

for(int i = 0; i != n; ++i)printf(ans[i] ? "YES\n" : "NO\n"); //fout<<(ans[i]?"YES":"NO")<<endl;

return 0;
}
int find(int root) {
return root == fa[root] ? root : fa[root] = find(fa[root]);
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值