首先读完题就能看出来,从后往前倒着处理操作 2 ,因为某时的操作 2 只会对它前面的数产生已影响。而要考虑到某个数受到后面多个操作,比如 1->2 , 2->3, 这样,就类似于找该条链上祖先节点,考虑到用并查集维护。
然后WA了...
仔细想了一下,并查集的Find操作会在一次查找后,将其路径上所有节点都连向祖先节点,这就有问题。比如现在从后往前有如下操作 2 :3->4, 2->3, 1->2, 那就是这样的:
那现在正好有个数是 1 ,我调用Find(1)查询其祖先,1、2、3就都直接连到 4 了。
那现在往前又有个操作 2->5:
问题来了,我之前调用了Find(1),1就连接到4了,而现在1是要变成5的,但是原来的(有向)边被我修改了,就出问题了。
#include<iostream>
#include<cstring>
#include<cstdio>
#include<cmath>
#include<vector>
using namespace std;
const int INF = 1e9+7;
const long long INFq = 1e18+7;
struct Query
{
int x,y,t;
Query(int a=0,int b=0,int c=0){ x=a;y=b;t=c; }
};
vector<Query>query;
int ans[500010];
int par[500010];
bool vis[500010];
int Find(int k)
{
int ret;
if( vis[k] || k==par[k] )ret = k;
else {
vis[k] = true;
ret = Find(par[k]);
vis[k] = false;
}
return ret;
}
void Trans(int x,int y)
{
x = Find(x); y=Find(y);
if(x!=y){
par[x] = y;
}
return ;
}
int main()
{
int T=1;
// scanf("%d",&T);
while(T--)
{
int n;
scanf("%d",&n);
int op,x,y;
int tot=0;
for(int i=0;i<n;i++)
{
scanf("%d",&op);
if(1==op){
tot++;
scanf("%d",&x);
ans[tot] = x;
}
else{
scanf("%d %d",&x,&y);
query.push_back( Query(x,y,tot) );
}
}
for(int i=1;i<500010;i++)par[i] = i;
int k = query.size()-1;
for(int i=tot; i>0; i--)
{
while( k>=0 && query[k].t >= i ){
par[query[k].x] = par[query[k].y];
// Find(query[i].x);
k--;
}
ans[i] = Find(ans[i]);
// ans[i] = par[ans[i]];
}
for(int i=1;i<=tot;i++)printf("%d ",ans[i]);
printf("\n");
}
}
/*
4
1 1
2 1 2
2 3 4
2 2 3
*/
再想着要不把Find优化去了,看能不能先把1000的test4过了,结果还是WA...
WA的原因见代码最下面注释的数据,具体为啥还有点模模糊糊的,因为这题的操作的先后顺序是有影响的。
题解代码的 par[x] = par[y] 可行,因为这题不需要合并两棵树(我之前还合并了错的更离谱)