题意:有n个点,m条边,边有c种颜色,q次操作。
每个边都有一种颜色。
然后操作有两种,一种是再加一条边,另一种是查询能否从x达到y。
移动的限制是,连着走两步必须是同一种颜色,如果走奇数步,最后一步可以是任意颜色。
例子:1-2-3-4-5-6。
这个题颜色种类很多,我是用map<int,vector<int>>来存边。
我们首先可以想到 对于点x同种颜色连着的点都是可以相互移动的,所以我们可以用这种方法将它们用并查集合并,我们也可以直接map<int,int>来存边,因为一个vector里的点是等价的。
但是这样有一个问题,走偶数步可以走通的已经被我们合并可以直接查询了,那么最后一步能走到的怎么办呢?
我们可以对每个联通集合存一个set,里面放集合中能一步走到的点。我们之后要合并集合,这个set自然也要合并,本来是想着用可并堆,但是其实用启发式合并也可以了。
以下为代码:
#include<bits/stdc++.h> using namespace std; int i,i0,n,m,c,q,pre[100005]; map<int,vector<int>>mp[100005]; set<int>dic[100005]; int fin(int x){return (pre[x]==x)?x:pre[x]=fin(pre[x]);} void uni(int x,int y) { if(fin(x)!=fin(y)) { if(dic[fin(x)].size()<dic[fin(y)].size())swap(x,y); for(auto i:dic[fin(y)])dic[fin(x)].insert(i),pre[fin(y)]=fin(x); } } void add() { int x,y,col; scanf("%d %d %d",&x,&y,&col); dic[fin(x)].insert(y),dic[fin(y)].insert(x); mp[x][col].push_back(y),uni(y,mp[x][col][0]); mp[y][col].push_back(x),uni(x,mp[y][col][0]); } int main() { scanf("%d %d %d %d",&n,&m,&c,&q); for(i=1;i<=n;i++)pre[i]=i; while(m--)add(); while(q--) { char op; scanf(" %c",&op); if(op=='?') { int x,y; scanf("%d %d",&x,&y); printf("%s\n",(fin(x)==fin(y)||dic[fin(x)].count(y))?"Yes":"No"); } else add(); } return 0; }