题目分析:涉及到亲戚或者朋友关系,例如亲戚的亲戚是亲戚,朋友的朋友是朋友,敌人的敌人是朋友这类问题,几乎可以直接思考并查集。此题目显然也是并查集问题,但是题目的查询操作居然可以遗忘掉已有的关系再询问,这一点就很难处理。因为并查集可以并,但并没有删除操作。而且即便删除a和b的边,a和b仍然可能通过其他关系连通。解决这个问题是关键。
题解:为了解决遗忘关系后再查询的问题。此处使用了一种OI技巧,离线查询
离线查询就是把q个询问先保存起来,然后一次性处理他们,而不是按询问次序处理。
举个例子说明下,一个序列a1,a2,.......an,现在有q个查询q1,q2,q3.......,询问你前qi个数字的最大值,比如查询序列是5 3 2 7......,先得到前5个最大值,再得到前3个最大值,我们知道求前5个最大值过程中,一定是先求得前3个最大值,那么可以先把这套查询序列存起来排个序2 3 5 7 ,从前向后求最大值,求到2 3 5 7 时把答案存起来以备未来查询使用。
(1)前面m个关系先不要进行并查集建树运算,而是保存在一个set中(方便查询)。
(2)我们先把所有的q个询问都保存起来,将那些遗忘的关系都找出来,如果这个关系在前m个关系中存在,就将set中这个关系删除,不存在就是无效关系,也需标记一下(以后就不理这个关系了)。处理结束,set中保存的就是未来不会遗忘的关系,此时并查集建树。
(3)q个询问从后往前进行处理,如果是询问,就并查集中查找。如果qi是遗忘的关系,那么这个关系对于前面的q1至qi-1询问来说并没有遗忘,将其加入到并查集中。
(4)一些其他特殊的点。数据n=1e9,没法用数组存储并查集,只能用map来存储。样例很良心地给了一个需要特殊注意的点:遗忘的关系可能本就是不存在的;另外需要注意的是关系的对称性,关系(1,2)和(2,1)是相同的。
#include <bits/stdc++.h>
typedef long long ll;
using namespace std;
int n,m,q,op[100005][3];/**< op数组存储q个询问 */
set<pair<int,int> > s;/**< 存储关系 */
map<int,int>f;/**< 并查集数组用map存储,因为此题目数据范围是1e9,不能用途数组 */
int findx(int x)/**< 并查集查函数 */
{
if(f[x]==0)/**< 如果是首次调用查函数,进行f[x]赋值。 */
f[x]=x;
return f[x]==x?x:f[x]=findx(f[x]);
}
int main()
{
ios::sync_with_stdio(0),cin.tie(0);
int i,j,p,x,y;
cin>>n>>m>>q;/**< 本题目n很大,所以不能像往常那样循环对所有f[i]赋值。 */
// for(i=1; i<=n; i++)
// f[i]=i;
for(i=1; i<=m; i++)/**< 把开始的关系set存起来 */
{
cin>>x>>y;
if(x>y)/**< 处理下次序,这样避免重复存储,也方便查询 */
swap(x,y);
s.insert({x,y});
}
for(i=1; i<=q; i++)
{
cin>>p>>x>>y;
if(x>y)/**< 同上 */
swap(x,y);
op[i][0]=p,op[i][1]=x,op[i][2]=y;
if(op[i][0]==1)
{
if(s.count({x,y}))/**< 1是遗忘,此处在set集合中去掉这个关系 */
s.erase({x,y});
else
op[i][0]=0;/**< 如果set没有这个关系,那么标记下这个关系无效,后续不会处理 */
}
}
for(auto it=s.begin(); it!=s.end(); it++)/**< 将遗忘后所有关系并查集 */
{
int f1=findx(it->first),f2=findx(it->second);
if(f1!=f2)
f[f1]=f2;
}
vector<string>ans;/**< 逆序存储答案 */
for(i=q; i>=1; i--)
{
if(op[i][0]==1) /**< 把第i个关系加回去,对于i-1之前的询问,这个关系是有效的 */
{
int f1=findx(op[i][1]),f2=findx(op[i][2]);
if(f1!=f2)
f[f1]=f2;
}
else if(op[i][0]==2)
{
int f1=findx(op[i][1]),f2=findx(op[i][2]);
if(f1==f2)
ans.push_back("Yes");
else
ans.push_back("No");
}
}
for(i=ans.size()-1; i>=0; i--)
cout<<ans[i]<<endl;
return 0;
}