题意:
给定原来孤立的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; }