时间限制:C/C++ 1秒,其他语言2秒 空间限制:C/C++ 262144K,其他语言524288K 64bit IO Format: %lld
题目描述
小牛牛在暑假的时候返乡务农。为了响应习主席“绿水青山就是金山银山”的号召开始种树。
但他种树的方式比较奇怪。他会先拿出一些有标号的点,然后他会有一些操作,分为3种:添加一条边,切断一条边,询问他有多少个大小不为一的树。当然,年幼的小牛牛记性不是很好,他有时会连出重边,有时也会切断根本不存在的边。当然,对于这样的操作,无视就可以了。
注意看数据范围
输入描述:
第一行一个n
接下来n行,表示n次操作。
每组操作格式如下
1 u v 表示将u点和v点连起来
2 u v 表示将u点和v点之间的边删除
3 表示询问
输出描述:
对每一个3询问输出一个数表示当前大小不为一的树的数量。
示例1
输入
复制
4 1 1926 817 3 2 817 1926 3
输出
复制
1 0
备注:
对于全部数据 1≤n≤10^5^,保证不被忽略的操作1不构成环。1≤u,v≤10^8^
题解:
根据题意可知,就是一个建树拆树的过程,我们可以完全模拟进行
用到set来实现
我们先分析建树,当连接u和v两个点时,会有如下情况:
1.u和v只是两个点,连接后sum++
2.u和v分别是两个树上的点,连接后两树合并为一树,sum - -
3.u和v已连接,那就没有任何影响
sum为树的数量
拆树时(就是建树相反):
1.如果两点未连接,那就没有任何影响
2.如果与u和v连接的点均大于2,也就说明u和v连接前就已经与其他点相连,拆开后一树变两树sum++
3.如果u和v均为彼此相连,拆开后sum--
查询时直接输出结果
map的find和size可以帮助我们实现以上要求
注意:
点的大小并非按顺序,所以即可离散化处理
具体内容看代码
代码:
#include<bits/stdc++.h> #include<set> using namespace std; const int maxn=1e6+9; int op[maxn]; int c1[maxn],c2[maxn],lsh[maxn]; set<int>tree[maxn]; int main() { int n; cin>>n; int cnt=0; for(int i=1;i<=n;i++) { cin>>op[i]; if(op[i]==3)continue; else { cin>>c1[i]>>c2[i]; lsh[++cnt]=c1[i]; lsh[++cnt]=c2[i]; } } sort(lsh+1,lsh+1+cnt); int ant=unique(lsh+1,lsh+1+cnt)-lsh-1; for(int i=1;i<=n;i++) { c1[i]=lower_bound(lsh+1,lsh+1+ant,c1[i])-lsh; c2[i]=lower_bound(lsh+1,lsh+1+ant,c2[i])-lsh; } int sum=0; for(int i=1;i<=n;i++) { if(op[i]==1) { int u=c1[i],v=c2[i]; if(tree[u].find(v)!=tree[u].end())continue;//已存在此边 if(tree[u].size()==0&&tree[v].size()==0)sum++; if(tree[u].size()&&tree[v].size())sum--; tree[u].insert(v); tree[v].insert(u); } else if(op[i]==2) { int u=c1[i],v=c2[i]; if(tree[u].find(v)==tree[u].end())continue;//还没有这个边 if(tree[u].size()>=2&&tree[v].size()>=2)sum++; if(tree[u].size()==1&tree[v].size()==1)sum--; tree[u].erase(v); tree[v].erase(u); } else { cout<<sum<<endl; } } return 0; }