赞
踩
题目分析:涉及到亲戚或者朋友关系,例如亲戚的亲戚是亲戚,朋友的朋友是朋友,敌人的敌人是朋友这类问题,几乎可以直接思考并查集。此题目显然也是并查集问题,但是题目的查询操作居然可以遗忘掉已有的关系再询问,这一点就很难处理。因为并查集可以并,但并没有删除操作。而且即便删除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;
- }
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。