题意:有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;
}