题意:
给定原来孤立的n个点。
然后进行两种操作:
E i:查询第i个点到它的根节点的距离(mod1000),孤立点的根节点是它自己。
I i j:将节点i接到节点j上。
思路:
并查集。但是要申请一个数组来代表每个点的权值,优化一下时间,否则TLE。
其实优化的本质按标准范例说明如下:
如果是没有权值的并查集构成的链为3->1->2->4这样。查询3的时候都从3到4要运算3次加法,显然耗时。
优化之后:刚开始是3->1,dis[3]=2,然后因为操作I 1 2得1->2,且dis[1]=|1-2|mod1000=1,的时候将3->1也转化为3->2,同时dis[3]=dis[3]+dis[1];2是根节点所以dis[2]=0.
即本质是将每个并查集构成的树都转化为一个深度=1,即每个节点都和它的根节点直接相连的树,因为如果这个树恶化成一条链,这条链的耗时很大。
挺好的一道题目。
#include<iostream>
#include<algorithm>
#define max(a,b) (a>b?a:b)
#define abs(a) ((a)>0?(a):-(a))
#define min(a,b) (a<b?a:b)
using namespace std;
const int N=20005;
int n;
int root[N];
int dis[N];
char oper;
int query(int x)
{
if(root[x]==x)
{
return x;
}
int ret=query(root[x]);
dis[x]=(dis[x]+dis[root[x]]);//要在query函数之后,因为每次query函数都会更新dis值.
root[x]=ret;
return ret;
}
int main()
{
int cases;
scanf("%d",&cases);
while(cases--)
{
memset(dis,0,sizeof(dis));
scanf("%d",&n);
for(int i=1;i<=n;i++)
root[i]=i;
while(getchar(),scanf("%c",&oper),oper!='O')
{
int x,y;
if(oper=='E')
{
scanf("%d",&x);
query(x);
printf("%d\n",dis[x]);
}
else
{
scanf("%d%d",&x,&y);
root[x]=y;
dis[x]=(abs(x-y))%1000;
}
}
}
return 0;
}