POJ2236-Wireless Network
开始刷并查集,不打算刷多,就大概AC人数300以上的题吧(时间不咋多了,其他以后再说),数一巨巨acm路线
POJ 10000MS、65536K —— 这题10s
名字叫无线网络,想起之前刷过的题名一样但不是一个题,之前自己写的东西太稚嫩了傻逼一样。这道题让我想到了之前刷的拓扑排序奶牛比赛题目(脑子啥也想不起来了,AC这道题后再回顾奶牛比赛吧)
既然到了并查集想下之前学过的东西,搜索肯定没啥思路,那最短路,题目说不会超过3*105行,总共1001个点,那最多会有3*105-1001行的操作让你判断。
而每次判断,点数N是1001个,那最多: 条边,用堆优化迪杰斯特拉,只不过做个改动,松弛更新加入队列的时候仅仅是小于距离D才能加入(只判断到某点距离是不行的),所以最后只看某点有没有被松弛过即可。复杂度,3*105-1001算作3*105,再乘堆优化迪杰斯特拉mlogN,m是边数N^2级别,总共,1011,(乘3*105是因为每次修好点都要重新建图然后跑最短路算法吧我觉得)贝尔曼/SPFA也是一样的。突然发现这只是1点出发的,如果问你2 3是否可以通信,完犊子,爆炸,都会超时,故去学并查集
嘎嘎高潮博客,面向对象的C++还是Java我也不晓得,但代码意思懂了
没啥思路,打算每修好一个点就遍历所有点,发现有点距离他D以内,就tm加到那个点的根上,都没有就另开个自己作为根。这是动态的,加不影响前面的,每一个只要跟集合(根下的)里的任何一个点距离小于D,就可以直接加在根下,可以与根下所有点通信。不同于最短路,每个结果都与其他点有关,毕竟可能出现我走某点再走某点到你的情况,点增加就会导致我可能走其他点就到你会更近,破坏原有的答案,即最短路算法所求的是最短,而并查集不需要什么最不最短,跟集合内任意点有关就可以加进来,相对宽松不少,所以用最短路就会大材小用(由于跑出的结果更“苛刻”但本不需要)导致可以求的数量级没并查集多,导致用最短路会超时.
照着高潮博客里的图试着写,大概X次插点3*105-X次查两点的根
感觉考验的不是算法本身,对我来说写起来更多麻烦的地方是语法,进一步说是C++vector的用法概念理解
写完第一次提交(注释相当值得看)

1 #include<string.h> 2 #include<iostream> 3 #include<string.h> 4 #include<vector> 5 #define MAX 1001+1 6 #include<math.h> 7 using namespace std; 8 int N,D; 9 struct Node{ 10 int x; 11 int y; 12 }node[MAX]; 13 vector<int>G; 14 int disj_set[MAX];//全称并查集disjoint_set[MAX],记录顶点,小于0为顶点,大于0为顶带你 15 int a,b; 16 //int rey[MAX];//已经修好了的点是哪个,全称already_reparied_v[MAX];,后来发现应该用vector 17 //vector<Node>node[MAX]; 18 int main() 19 { 20 while(cin>>N>>D) 21 { 22 G.clear();//没啥必要,不可能输入多组ND 23 memset(disj_set,-1,sizeof(disj_set));//按字节赋值,-1是巧合,姑且这样用 24 for(int i=1;i<=N;i++){//修的点是从1开始的,4个点1234,所以i从1开始 25 cin>>a>>b; 26 node[i].x=a; 27 node[i].y=b; 28 } 29 char str; 30 double dis; 31 int disx; 32 int disy; 33 34 // int re_num=0;//已经修好的点数already_repaired_num,有了vector这个也不必要了 35 while(cin>>str){//只要输入ctrl+Z整个程序就会结束,并不会跳到外面等着输入下一波ND 36 if(str=='O'){ 37 cin>>a;//a代表第几个,从1开始,上面输入的数据node[i]]也是从1开始记录的 38 for(int i=0;i<G.size();i++){//只需要获取G.size,且size默认就是0到size-1 39 cout<<"#"<<G.size()<<endl; 40 // disx=pow((G[i].x-G[a].x),2);//i和a是第几个点,而G[i],G[a]就是可以.x.y将第几个点的横纵坐标弄出来,比如i是1,那我G[1].x就是上面输入的时候记录的node[i].x,艹才发现代码写错的,G他妈就是int,修改见下面一行 41 disx=pow((node[G[i]].x-node[a].x),2);//下标是最难想的。回忆之前刷的最短路memset是最难想的。初中考试语文试卷写一堆想法,思维 42 disy=pow((node[G[i]].y-node[a].y),2); 43 dis=sqrt(disx+disy); 44 if(dis<=D){ 45 if(G[i]<a){//遍历到a我之前的,之前肯定根都弄好了我直接跟他们一样即可 46 int mod=G[i]; 47 while(disj_set[mod]>0){ 48 mod=disj_set[mod]; 49 } 50 //此时mod是顶点 51 disj_set[mod]=disj_set[mod]+disj_set[a];//吸新来的血,-1是血 52 disj_set[a]=mod;//插点做并查集 新来的顶点是mod 53 // G.push_back(a); 54 cout<<"!"<<disj_set[1]<<" "<<" "<<disj_set[2]<<" "<<disj_set[3]<<" "<<disj_set[4]<<endl; 55 // break;//不应该break,因为加1加2加4后,1 4都不行,那加3后1 4就行了,如果break了,就把4甩一边了,应该把4也更新进来,根和他们123一样 56 } 57 if(G[i]>a){//我之后的比如4,也应该更新,此时就应该是i更新了 58 int mod=a;//该将a的根给后面那些“因为我a的加入而能跟前面的点有连接”的点了 59 while(disj_set[mod]>0){ 60 mod=disj_set[mod]; 61 } 62 cout<<"mod"<<mod<<endl; 63 //此时mod是新加入点a顶点 64 disj_set[mod]=disj_set[mod]+disj_set[G[i]];//吸新来的血,-1是血 65 disj_set[G[i]]=mod;//插点做并查集 新来的顶点是mod 66 cout<<"@"<<disj_set[1]<<" "<<" "<<disj_set[2]<<" "<<disj_set[3]<<" "<<disj_set[4]<<endl; 67 } 68 } 69 } 70 G.push_back(a); 71 } 72 if(str=='S'){ 73 cin>>a>>b;//判断ab根是否相同 74 int mod_a=a; 75 int mod_b=b; 76 while(disj_set[mod_a]>0){ 77 mod_a=disj_set[mod_a]; 78 } 79 while(disj_set[mod_b]>0){ 80 mod_b=disj_set[mod_b]; 81 } 82 if(mod_a==mod_b && mod_a!=-1) 83 cout<<"SUCCESS"<<endl; 84 else 85 cout<<"FALL"<<endl; 86 } 87 } 88 } 89 }
简洁版:

1 #include<string.h> 2 #include<iostream> 3 #include<string.h> 4 #include<vector> 5 #define MAX 1001+1 6 #include<math.h> 7 using namespace std; 8 int N,D; 9 struct Node{ 10 int x; 11 int y; 12 }node[MAX]; 13 vector<int>G; 14 int disj_set[MAX]; 15 int a,b; 16 int main() 17 { 18 while(cin>>N>>D) 19 { 20 G.clear(); 21 memset(disj_set,-1,sizeof(disj_set)); 22 for(int i=1;i<=N;i++){ 23 cin>>a>>b; 24 node[i].x=a; 25 node[i].y=b; 26 } 27 char str; 28 double dis; 29 int disx; 30 int disy; 31 32 while(cin>>str){ 33 if(str=='O'){ 34 cin>>a; 35 for(int i=0;i<G.size();i++){ 36 disx=pow((node[G[i]].x-node[a].x),2); 37 disy=pow((node[G[i]].y-node[a].y),2); 38 dis=sqrt(disx+disy); 39 if(dis<=D){ 40 if(G[i]<a){ 41 int mod=G[i]; 42 while(disj_set[mod]>0){ 43 mod=disj_set[mod]; 44 } 45 disj_set[mod]=disj_set[mod]+disj_set[a]; 46 disj_set[a]=mod; 47 } 48 if(G[i]>a){ 49 int mod=a; 50 while(disj_set[mod]>0){ 51 mod=disj_set[mod]; 52 } 53 disj_set[mod]=disj_set[mod]+disj_set[G[i]]; 54 disj_set[G[i]]=mod; 55 } 56 } 57 } 58 G.push_back(a); 59 } 60 if(str=='S'){ 61 cin>>a>>b; 62 int mod_a=a; 63 int mod_b=b; 64 while(disj_set[mod_a]>0){ 65 mod_a=disj_set[mod_a]; 66 } 67 while(disj_set[mod_b]>0){ 68 mod_b=disj_set[mod_b]; 69 } 70 if(mod_a==mod_b && mod_a!=-1) 71 cout<<"SUCCESS"<<endl; 72 else 73 cout<<"FALL"<<endl; 74 } 75 } 76 } 77 }
WA了(发现是“FAIL”,改了还是WA)
~~~~(>_<)~~~~好受打击,POJ的discuss都说是大水题~~~~(>_<)~~~~

1 #include <iostream> 2 #include <stdlib.h> 3 #include <time.h> 4 #include<stdio.h> 5 #include<iostream> 6 #include<time.h> 7 #include <string> 8 #include <random> 9 using namespace std; 10 int a,b; 11 int main(){ 12 srand(time(0) + (unsigned long long)(new char)); 13 int n = rand()%5+1; 14 int d = rand()%5+1; 15 cout<<n<<" "<<d<<endl; 16 for(int i=1;i<=n;i++){ 17 int a = rand()%6; 18 int b = rand()%4; 19 cout<<a<<" "<<b<<endl; 20 } 21 for(int i=1;i<=4;i++){//假设就O四个 22 // while((a=rand()%n)==0){ 23 // ; 24 // } 25 a=rand()%n+1; 26 cout<<"O "<<a<<endl; 27 } 28 for(int i=1;i<=3;i++){ 29 a=rand()%n+1; 30 b=rand()%n+1; 31 cout<<"S "<<a<<" "<<b<<endl; 32 } 33 }
生成数据:
5 5 0 3 0 3 1 0 2 2 4 3 O 5 O 3 O 1 O 3 S 2 4 S 3 2 S 2 5
草你奶奶我是不是理解错题了,AC代码都输出仨FAIL,我举得因该是FAIL、SUCCESS、SUCCESS啊。我考虑多了,重边也算上了,其实2虽然没修,但1修了,那么2也就好了,S 3 2自然就好了 —— 后更新:确实理解错了,坏了就是通讯不了,至于为啥能SUCCESS,在if里的两个if那打印5个disj_set就知道了,同下。但重边这个事貌似并不是WA的原因
再生成数据
4 5 4 1 3 1 0 1 1 1 O 3 O 2 O 2 O 1 S 2 4 S 4 2 S 1 2
发现我的代码卡住了
调试下

1 #include<string.h> 2 #include<iostream> 3 #include<string.h> 4 #include<vector> 5 #define MAX 1001+1 6 #include<math.h> 7 using namespace std; 8 int N,D; 9 struct Node{ 10 int x; 11 int y; 12 }node[MAX]; 13 vector<int>G; 14 int disj_set[MAX]; 15 int a,b; 16 int vis[MAX]; 17 int main() 18 { 19 while(cin>>N>>D) 20 { 21 G.clear(); 22 memset(disj_set,-1,sizeof(disj_set)); 23 for(int i=1;i<=N;i++){ 24 cin>>a>>b; 25 node[i].x=a; 26 node[i].y=b; 27 } 28 char str; 29 double dis; 30 int disx; 31 int disy; 32 33 while(cin>>str){ 34 if(str=='O'){ 35 cin>>a; 36 for(int i=0;i<G.size();i++){ 37 disx=pow((node[G[i]].x-node[a].x),2); 38 disy=pow((node[G[i]].y-node[a].y),2); 39 dis=sqrt(disx+disy); 40 if(dis<=D){ 41 if(G[i]<a){ 42 cout<<"a"<<endl; 43 int mod=G[i]; 44 while(disj_set[mod]>0){ 45 mod=disj_set[mod]; 46 } 47 disj_set[mod]=disj_set[mod]+disj_set[a]; 48 disj_set[a]=mod; 49 } 50 if(G[i]>a){ 51 cout<<"b"<<endl; 52 int mod=a; 53 while(disj_set[mod]>0){ 54 mod=disj_set[mod]; 55 } 56 disj_set[mod]=disj_set[mod]+disj_set[G[i]]; 57 disj_set[G[i]]=mod; 58 } 59 cout<<"c"<<endl; 60 } 61 cout<<"d"<<endl; 62 } 63 G.push_back(a); 64 } 65 if(str=='S'){ 66 cin>>a>>b; 67 int mod_a=a; 68 int mod_b=b; 69 while(disj_set[mod_a]>0){ 70 mod_a=disj_set[mod_a]; 71 } 72 while(disj_set[mod_b]>0){ 73 mod_b=disj_set[mod_b]; 74 } 75 if(mod_a==mod_b && mod_a!=-1) 76 cout<<"SUCCESS"<<endl; 77 else 78 cout<<"FAIL"<<endl; 79 } 80 } 81 } 82 }
画出图来发现死循环了了,1下面的-1下面的1,始终指向自己
原因是在第二个“O 2”的时候1 2 3 4分别disj_set是-1 -2 2 -1,即2是根下面有个3(总共两个点所以是-2,负数表示顶点),3根是指向2,而将“O 2”加进来的时候,2会和G容器里已经有的3和2进行交互比较,先跟3交互的时候,误判3的根disj_set是-1,但其实是2,因为是指向2嘛,把这个disj_set[3]就加到了2上,这时候2,作为根本身应该变为绝对值更大的负数,结果现在变成0了
尝试加一个判断,你的根是-1或者你的根并不是我的根(意思是我们两棵树可以合并到一起了),就加到同一个根上(也不用大小比较了,之前被样例“误导”以为只要小于就一定被加过根,样例是1 2 4然后加的3),统称为只要你的根<0,就把根合过来(无论你是自己还是一堆),md又有种发明代码的感觉,好像直接看题解背模板,但经历坎坷越多,理解越深,坚决不看题解代码 —— 很多思路都是上面那个嘎嘎高潮博客里讲的
简单来说就是之前的思路是:误以为只要你数字比我大,比如第4个点是比第3个点大的,因为4比3大,(当然,这是之前的错误思路),我就误认为是你4没根,要更新你的根。其实就已经把disj_set是正的给加了,破坏的数据
整理下问题只需要考虑几点:
①、新修的点之前没修过,能找到跟他小于D的点,直接挂那个点的根上,样例的点坐标不变,只不过修完1,2后修3,3直接找2的根1,挂1上,即disj_set是负数就搞到别人的根上,严谨点说是disj_set是-1且没修过,就搞到别人根上
②、新修的点之前没修过,跟他小于D的点之前只有那个点自己,样例的点坐标不变,只不过修完1,2,4后修3,遍历G容器的时候,遍历到2,3挂1上,disj_set是-1且没修过,就搞到别人根上,遍历到4,将3挂4上显然不对,想起高潮博客里的,将4挂3的根1上,即之前修过(4)且也不是新修的这个点(3),但disj_set[4]是-1且由于这次新修的点(3)导致他(4)可以跟人通信了,就搞到别人根上
③、之前修过,找到了根(能跟别人通信),就别动了,即disj_set是正数且之前修过就跳过。或者已经有了团体自己就是负数,不过跟我主根不同
④、之前修过,但依旧没根(严谨点说是只有自己,disj_set是-1),其实也不用管,必定还是没法跟别人通信,即disj_set是-1但之前修过,就跳过,如果有可能被找到根去归集,那一定不会到现在还是-1,肯定有点会把他连上通信,即②,所以目前G里面没有你能通信的,再修也白扯
⑤、测试通信与否的数据点没修过,但有坐标和他一样的修复过,这个根据对拍博客得知POJ没这种数据,因为题目描述没说两台计算机的坐标不能相同,意思就是第1台计算机坐标(2,3),第2台计算机坐标(2,3),第3台计算机坐标(3,3),爹只修了2和3台,问你1 3 能否通信,AC了只能说POJ数据太水太垃圾。这个好说,只要判断的时候不是判断已经修复的“第几个”点,而是判断已经修复的坐标 —— 更新:坏了就是没法通信
总结一下,两点之间距离<D:
①disj_set[x]是-1,vis[x]是0,搞到别人根上,
②disj_set[y]是-1,vis[y]是1,搞到别人根上。
③disj_set[y]>0,vis[y]是1,不动,动就会错。或者disj_set[y]是不等于-1的负数,证明他有了自己的团体,且你的根不是我,搞到我根上
④disj_set[y]是-1,vis[y]是1,不动,动了也不会有数据更新,因为不会<D根本就
总结,遍历的时候,比较新修的x和G容器里有的y,只要距离<=D:
①disj_set[x]是-1,vis[x]是0,搞到y根上
②disj_set[x]是不等于-1的负数,证明他有了自己的团体,且你x的根不是我y的根,搞到y根上(想想其实不可能,因为可能的早就被更新了,或者说如果是不等于-1的负数,那x的根一定是y的根,如果不是,那一定距离>D)
③disj_set[y]是-1,vis[y]是1,搞到x根上
④disj_set[y]是不等于-1的负数,证明他有了自己的团体,且你y的根不是我x的根,y搞到x根上,写起来可以跟③合并
写完WA,代码注释很值得看,有很多WA后根据对拍数据进行改动

1 #include<string.h> 2 #include<iostream> 3 #include<string.h> 4 #include<vector> 5 #define MAX 1001+1 6 #include<math.h> 7 using namespace std; 8 int N,D; 9 struct Node{ 10 int x; 11 int y; 12 }node[MAX]; 13 vector<int>G; 14 int disj_set[MAX]; 15 int a,b; 16 int main() 17 { 18 while(cin>>N>>D) 19 { 20 G.clear(); 21 memset(disj_set,-1,sizeof(disj_set)); 22 for(int i=1;i<=N;i++){ 23 cin>>a>>b; 24 node[i].x=a; 25 node[i].y=b; 26 } 27 char str; 28 double dis; 29 long long int disx; 30 long long int disy; 31 32 while(cin>>str){ 33 if(str=='O'){ 34 cin>>a; 35 for(int i=0;i<G.size();i++){ 36 disx=pow((node[G[i]].x-node[a].x),2); 37 disy=pow((node[G[i]].y-node[a].y),2); 38 dis=sqrt(disx+disy); 39 if(dis<=D){ 40 cout<<a<<" "<<G[i]<<endl; 41 // if(dis==0) 42 // continue;//如果加的是同一个点,且都是-1,就别动了,WA对拍出来的 43 // 反例: 44 //2 1 45 //4 0 46 //3 2 47 //O 2 48 //O 2 49 //O 2 50 //O 2 51 //S 1 1 52 //S 2 2 53 //S 2 1 54 //但这样写不行 55 //反例 56 //3 3 57 //5 2 58 //5 2 59 //1 3 60 //O 1 61 //O 2 62 //O 1 63 //O 1 64 //S 1 3 65 //S 1 3 66 //S 2 1 67 //两个点坐标一样呢 68 if(a==G[i]) 69 continue; 70 71 if(disj_set[a]==-1){ 72 int mod=G[i]; 73 while(disj_set[mod]>0) 74 mod=disj_set[mod]; 75 disj_set[mod]=disj_set[mod]+disj_set[a]; 76 disj_set[a]=mod; 77 cout<<"!"<<a<<" "<<G[i]<<" "<<disj_set[1]<<" "<<disj_set[2]<<" "<<disj_set[3]<<" "<<disj_set[4]<<" "<<disj_set[5]<<endl; 78 } 79 if(disj_set[G[i]]<0){ 80 int mod_a=a; 81 while(disj_set[mod_a]>0){ 82 mod_a=disj_set[mod_a]; 83 } 84 int mod_b=G[i]; 85 while(disj_set[mod_b]>0) 86 mod_b=disj_set[mod_b]; 87 if(mod_b!=mod_a){//根不同 88 disj_set[mod_a]=disj_set[mod_a]+disj_set[mod_b]; 89 disj_set[G[i]]=mod_a; 90 } 91 cout<<"@"<<a<<" "<<G[i]<<" "<<disj_set[1]<<" "<<disj_set[2]<<" "<<disj_set[3]<<" "<<disj_set[4]<<" "<<disj_set[5]<<endl; 92 } 93 //你妈逼忘了个事两个都是正数也可以联姻 94 // 反例 95 //5 3 96 //1 3 97 //1 1 98 //3 0 99 //5 3 100 //5 0 101 //O 2 102 //O 4 103 //O 3 104 //O 5 105 //S 2 2 106 //S 3 5 107 //S 5 1 108 // 那我觉得代码可以都重写了,只要根不同就嫁接联姻 109 } 110 } 111 G.push_back(a); 112 } 113 if(str=='S'){ 114 cin>>a>>b; 115 int mod_a=a; 116 int mod_b=b; 117 if(N==1)//如果只有一个点,那下面死循环,所以加个特判,WA好几次对拍出来的 118 cout<<"SUCCESS"<<endl; 119 else{ 120 while(disj_set[mod_a]>0) 121 mod_a=disj_set[mod_a]; 122 while(disj_set[mod_b]>0) 123 mod_b=disj_set[mod_b]; 124 125 if(mod_a==mod_b) 126 cout<<"SUCCESS"<<endl; 127 else 128 cout<<"FAIL"<<endl; 129 130 } 131 } 132 } 133 } 134 }
最后感觉,根本不用判断disj_set的正负数,只要根不同就嫁接联姻

1 #include<string.h> 2 #include<iostream> 3 #include<string.h> 4 #include<vector> 5 #define MAX 1001+1 6 #include<math.h> 7 using namespace std; 8 int N,D; 9 struct Node{ 10 int x; 11 int y; 12 }node[MAX]; 13 vector<int>G; 14 int disj_set[MAX]; 15 int a,b; 16 int main() 17 { 18 while(cin>>N>>D) 19 { 20 G.clear(); 21 memset(disj_set,-1,sizeof(disj_set)); 22 for(int i=1;i<=N;i++){ 23 cin>>a>>b; 24 node[i].x=a; 25 node[i].y=b; 26 } 27 char str; 28 double dis; 29 long long int disx; 30 long long int disy; 31 32 while(cin>>str){ 33 if(str=='O'){ 34 cin>>a; 35 for(int i=0;i<G.size();i++){ 36 disx=pow((node[G[i]].x-node[a].x),2); 37 disy=pow((node[G[i]].y-node[a].y),2); 38 dis=sqrt(disx+disy); 39 if(dis<=D){ 40 int mod_a=a; 41 while(disj_set[mod_a]>0)mod_a=disj_set[mod_a]; 42 43 int mod_b=G[i]; 44 while(disj_set[mod_b]>0)mod_b=disj_set[mod_b]; 45 46 if(mod_b!=mod_a){//根不同 47 disj_set[mod_a]=disj_set[mod_a]+disj_set[mod_b]; 48 disj_set[G[i]]=mod_a; 49 } 50 cout<<a<<" "<<G[i]<<"@"<<disj_set[1]<<" "<<disj_set[2]<<" "<<disj_set[3]<<" "<<disj_set[4]<<" "<<disj_set[5]<<endl; 51 } 52 } 53 G.push_back(a); 54 } 55 56 if(str=='S'){ 57 cin>>a>>b; 58 int mod_a=a; 59 int mod_b=b; 60 while(disj_set[mod_a]>0) 61 mod_a=disj_set[mod_a]; 62 while(disj_set[mod_b]>0) 63 mod_b=disj_set[mod_b]; 64 if(mod_a==mod_b) 65 cout<<"SUCCESS"<<endl; 66 else 67 cout<<"FAIL"<<endl; 68 } 69 } 70 } 71 72 } 73 //妈的忘了个事,将其他数合并过来的时候,应该将那数根下所有的也都更改下下标 74 //反例 75 //5 2 76 //2 2 77 //4 0 78 //5 1 79 //2 0 80 //0 2 81 //O 1 82 //O 4 83 //O 5 84 //O 2 85 //S 1 4 86 //S 4 4 87 //S 2 5
发现还不行,比如这么个反例:
1~5起初都是-1,经过几次后2可以连到3上,1,3又都可以连到4上,此时如果1和5再联通,那就要把1的根4连过去,此时需要修改5的下标为-5,修改1的下标5,还要修改2和3的下标,妈的看模板去了
发现好多都是用1~顺序存的初试根数组,没像高潮博客里的,我还弄了一天半时间艹
代码给我看的好挣扎太难受了,用的上面对拍博客里的第二个,我加了些打印内容方便自己理解

1 #include<iostream> 2 #include<cstdio> 3 #include<algorithm> 4 using namespace std; 5 6 int n,k; 7 bool repair[1005]; //为真则修好了 8 9 struct point{ 10 int pre; 11 int x,y; 12 }c[1005]; 13 14 void init() 15 { 16 for(int i=1;i<=n;i++){ 17 c[i].pre = i; 18 repair[i] = false; 19 } 20 } 21 22 int find(int x) 23 { 24 if(x==c[x].pre) 25 return x; 26 c[x].pre = find(c[x].pre); 27 return c[x].pre; 28 } 29 30 void unite(const point p1, const point p2) 31 { 32 int root1, root2; 33 root1 = find(p1.pre); 34 root2 = find(p2.pre); 35 cout<<"@"<<root1<<" "<<root2<<" 3pre:"<<c[3].pre<<endl; 36 if(root1 != root2) 37 if((p1.x - p2.x) * (p1.x - p2.x) + (p1.y - p2.y) * (p1.y - p2.y) <= k * k){ 38 c[root2].pre = root1; 39 cout<<"距离:"<<(p1.x-p2.x)*(p1.x-p2.x)+(p1.y-p2.y)*(p1.y-p2.y)<<" "<<k*k<<endl; 40 } 41 } 42 43 int main() 44 { 45 cin>>n>>k; 46 init(); 47 for(int i=1;i<=n;i++){ 48 scanf("%d%d",&c[i].x,&c[i].y); 49 } 50 char s[5]; 51 int a,b; 52 while(scanf("%s",s)!=EOF){ 53 if(s[0]=='A'){//字母太傻逼,改成A方便一步步打印理解代码的时候看懂代码 54 scanf("%d",&a); 55 repair[a] = true; 56 for(int i=1;i<=n;i++){ 57 if(i!=a && repair[i]){ 58 unite(c[a],c[i]); 59 cout<<a<<" "<<i<<"#"<<c[1].pre<<" "<<c[2].pre<<" "<< 60 c[3].pre<<" "<<c[4].pre<<" "<<c[5].pre<<endl; 61 } 62 } 63 } 64 else{ 65 scanf("%d%d",&a,&b); 66 if(find(a) == find(b)){ 67 printf("SUCCESS\n"); 68 } 69 else printf("FAIL\n"); 70 } 71 } 72 return 0; 73 } 74 //5 2 75 //2 2 76 //4 0 77 //5 1 78 //2 0 79 //0 2 80 //O 1 81 //O 4 82 //O 5 83 //O 2 84 //S 1 4 85 //S 4 4 86 //S 2 5 87 88 // 89 //5 1 90 //0 3 91 //0 1 92 //0 0 93 //0 2 94 //1 2 95 //A 2 96 //A 3 97 //A 4 98 //A 1 99 //A 5
用的测试数据:(O太影响调试的时候看打印数据,改用A了)
5 1 0 3 0 1 0 0 0 2 1 2 A 2 A 3 A 4 A 1 A 5
我画的草图(真的好心累,弄了好久好久都没懂)
看上面的控制台,我一直搞不懂,按理说每次c[].pre的改变都是那个距离小于D里弄的,表现到控制台就是输出“距离”那一行后输出“a i #”的那一行,才是有变化的,但实际c[3].pre在输入A 5(O不方便看)后的第二次遍历就从4变成了1,详细调试发现是在寻找2根的时候,在find函数里就被改变了,这有啥必要性么???找根咋还改变pre呢?去掉之后提交到数据强大的可用平台也能AC。发现叫压缩路径 —— 突然发现之前的菜鸟教程也挺不错的(一直以为是鸡肋)
感觉这人代码写的不好,既然有压缩路径,那为啥会有5 3 1 1 5这个结果,5为根,一层是1,二层是3和4,三层是2,说好的都挂在根上呢?就比如修1之前,结果已经是1 3 4 4 5了,现在修1,遍历第一个修好的点是2,这个2就 unite ,然后去 find 里找根了,这时候理应2的根由3变为4,但因为他写的是 root2 = find(p2.pre); 导致代入 unite 函数里找的其实是3的root
再去看看有没有更好的写法,算了,直接自己把他代码改吧(美其名曰优化别人代码,md这人写了个假路径压缩艹),改完发现可用平台从1280ms→60ms,牛逼耶以~

1 #include<iostream> 2 #include<cstdio> 3 #include<algorithm> 4 using namespace std; 5 int n,k; 6 bool repair[1005]; 7 struct point{ 8 int pre; 9 int x,y; 10 }c[1005]; 11 void init() 12 { 13 for(int i=1;i<=n;i++){ 14 c[i].pre = i; 15 repair[i] = false; 16 } 17 } 18 int find(int x) 19 { 20 if(x==c[x].pre) 21 return x; 22 c[x].pre = find(c[x].pre); 23 return c[x].pre; 24 } 25 void unite(int p1,int p2) 26 { 27 int root1, root2; 28 root1 = find(p1); 29 root2 = find(p2); 30 if(root1 != root2) 31 if((c[p1].x-c[p2].x)*(c[p1].x-c[p2].x)+(c[p1].y-c[p2].y)*(c[p1].y-c[p2].y)<=k*k) 32 c[root2].pre = root1; 33 } 34 35 int main() 36 { 37 cin>>n>>k; 38 init(); 39 for(int i=1;i<=n;i++){ 40 scanf("%d%d",&c[i].x,&c[i].y); 41 } 42 char s[5]; 43 int a,b; 44 while(scanf("%s",s)!=EOF){ 45 if(s[0]=='A'){//字母太傻逼,改成A方便一步步打印理解代码的时候看懂代码 46 scanf("%d",&a); 47 repair[a] = true; 48 for(int i=1;i<=n;i++){ 49 if(i!=a && repair[i]){ 50 // unite(c[a],c[i]); 51 unite(a, i); 52 //cout<<a<<" "<<i<<"#"<<c[1].pre<<" "<<c[2].pre<<" "<<c[3].pre<<" "<<c[4].pre<<" "<<c[5].pre<<endl; 53 } 54 } 55 } 56 else{ 57 scanf("%d%d",&a,&b); 58 if(find(a) == find(b)){ 59 printf("SUCCESS\n"); 60 } 61 else printf("FAIL\n"); 62 } 63 // cout<<endl; 64 } 65 return 0; 66 }
(注:上面代码无法AC,自行去掉无用的cout,自行将 if(s[0]=='A') 改成 if(s[0]=='O') 即可AC)
至此会了,再自己写一遍,一定不要回忆别人的代码哪怕自己改过AC的,忘记所有代码,只回忆算法思路(原因之前博客说过很多次了)
自己写的代码,但感觉还是残留之前印象,模板性太强,有点僵硬,估计变个样就不会了
AC代码
1 #include<stdio.h> 2 #include<string.h> 3 #include<iostream> 4 #define MAX 1001+1 5 #include<math.h> 6 using namespace std; 7 int N,D; 8 struct Node{ 9 int x; 10 int y; 11 int pre; 12 }c[MAX]; 13 int rep[MAX]; 14 void init() 15 { 16 for(int i=1;i<=N;i++){ 17 c[i].pre=i; 18 } 19 } 20 int find(int x) 21 { 22 if(x==c[x].pre) 23 return x; 24 c[x].pre=find(c[x].pre); 25 return c[x].pre; 26 27 } 28 void union_find(int a,int b)//union和struct一样是关键字 29 { 30 if(sqrt(pow((c[a].x-c[b].x),2)+pow((c[a].y-c[b].y),2))<=D){ 31 int root1=find(a); 32 int root2=find(b); 33 c[root1].pre=root2; 34 } 35 } 36 int main() 37 { 38 while(scanf("%d%d",&N,&D)!=EOF){ 39 memset(rep,0,sizeof(rep)); 40 for(int i=1;i<=N;i++){ 41 scanf("%d%d",&c[i].x,&c[i].y); 42 } 43 init();//初始化根 44 char str; 45 while(scanf("%c",&str)!=EOF){ 46 if(str=='O'){ 47 int a; 48 scanf("%d",&a); 49 rep[a]=1; 50 for(int i=1;i<=N;i++){ 51 if(rep[i]==1){ 52 if(i==a)//可有可无剪枝 53 continue; 54 union_find(i,a); 55 } 56 } 57 } 58 if(str=='S'){ 59 int a,b; 60 scanf("%d%d",&a,&b); 61 if(find(a)==find(b)) 62 printf("SUCCESS\n"); 63 else 64 printf("FAIL\n"); 65 66 } 67 } 68 } 69 }
POJ的discuss说要long不然WA纯属放屁
关于快读:
尝试下快读也没快多少,3.3s→2.7s,另外发现快读没法读字符,快读博客,但没法用,int可以,因为快读字符串写的是string型,如果while读的话,报错,因为while里面是bool类型,之前int快读就可以直接while(n=read());,写个while(true)然后一直输入字符获取O或S,也不对,获取的字符还无法和EOF比较,一个string一个int
加快读AC代码(没啥效果)

1 #include<stdio.h> 2 #include<string.h> 3 #include<iostream> 4 #define MAX 1001+1 5 #include<math.h> 6 #include<ctype.h>//给isdigit用的 7 using namespace std; 8 int N,D; 9 struct Node{ 10 int x; 11 int y; 12 int pre; 13 }c[MAX]; 14 int rep[MAX]; 15 inline int read() 16 { 17 char ch=getchar(); 18 if(ch==EOF)return 0;//没有这句话TLE 19 int x=0,w=1; 20 while(ch<'0'|| ch>'9') 21 { 22 if(ch=='-') w=-1; 23 ch=getchar(); 24 } 25 while(ch>='0' && ch<='9') 26 { 27 //代替x=x*10+ch-'0'; 28 x=(x<<1) + (x<<3) +(ch^48); 29 ch=getchar(); 30 } 31 return x*w; 32 } 33 inline string rd() 34 { 35 string str=""; 36 char ch=getchar(); 37 if(ch==EOF)return 0; 38 //处理空格或回车 39 while(ch==' ' || ch=='\n' || ch=='\r') 40 { 41 ch=getchar(); 42 } 43 //读入 44 while(ch!=' ' && ch!='\n' && ch!='\r') 45 { 46 str+=ch; 47 ch=getchar(); 48 } 49 return str; 50 } 51 void init() 52 { 53 for(int i=1;i<=N;i++){ 54 c[i].pre=i; 55 } 56 } 57 int find(int x) 58 { 59 if(x==c[x].pre) 60 return x; 61 c[x].pre=find(c[x].pre); 62 return c[x].pre; 63 64 } 65 void union_find(int a,int b)//union和struct一样是关键字 66 { 67 if(sqrt(pow((c[a].x-c[b].x),2)+pow((c[a].y-c[b].y),2))<=D){ 68 int root1=find(a); 69 int root2=find(b); 70 c[root1].pre=root2; 71 } 72 } 73 int main() 74 { 75 while(N=read()){ 76 D=read(); 77 memset(rep,0,sizeof(rep)); 78 for(int i=1;i<=N;i++){ 79 c[i].x=read(); 80 c[i].y=read(); 81 } 82 init();//初始化根 83 char str; 84 while(scanf("%c",&str)!=EOF){ 85 // cout<<str<<endl; 86 if(str=='O'){ 87 int a; 88 a=read(); 89 rep[a]=1; 90 for(int i=1;i<=N;i++){ 91 if(rep[i]==1){ 92 // if(i==a)//可有可无剪枝 93 // continue; 94 union_find(i,a); 95 } 96 } 97 } 98 if(str=='S'){ 99 int a,b; 100 a=read(); 101 b=read(); 102 if(find(a)==find(b)) 103 printf("SUCCESS\n"); 104 else 105 printf("FAIL\n"); 106 107 } 108 } 109 } 110 }
而且还有个事把POJ搞502好几次,快读的read里没有 if(ch==EOF)return 0; ,在main里写 while((N=read())!=EOF) 是没用的,本地ctrl+Z和Ctrl+C都没反应,POJ MLE多次OJ崩溃,必须main里直接 while((N=read())) 不可有!=EOF然后read函数里加上那句话。进一步测试发现,输入N不可以有EOF,然后输入O,S那块必须有EOF才可以结束和AC,但read函数里,也必须加那句EOF,奇怪结束与否不是只看输入O,S那块吗?为啥输入N的快读函数里去掉EOF就不会结束,难道tm是两个同时才行?
改成vector不每次遍历所有点,只遍历G.size,也没快多少,2.7s

1 #include<stdio.h> 2 #include<string.h> 3 #include<iostream> 4 #define MAX 1001+1 5 #include<vector> 6 #include<math.h> 7 #include<ctype.h>//给isdigit用的 8 using namespace std; 9 int N,D; 10 struct Node{ 11 int x; 12 int y; 13 int pre; 14 }c[MAX]; 15 vector<int>G; 16 //int rep[MAX]; 17 inline int read() 18 { 19 char ch=getchar(); 20 if(ch==EOF)return 0;//没有这句话TLE 21 int x=0,w=1; 22 while(ch<'0'|| ch>'9') 23 { 24 if(ch=='-') w=-1; 25 ch=getchar(); 26 } 27 while(ch>='0' && ch<='9') 28 { 29 //代替x=x*10+ch-'0'; 30 x=(x<<1) + (x<<3) +(ch^48); 31 ch=getchar(); 32 } 33 return x*w; 34 } 35 36 void init() 37 { 38 for(int i=1;i<=N;i++){ 39 c[i].pre=i; 40 } 41 } 42 int find(int x) 43 { 44 if(x==c[x].pre) 45 return x; 46 c[x].pre=find(c[x].pre); 47 return c[x].pre; 48 49 } 50 void union_find(int a,int b)//union和struct一样是关键字 51 { 52 if(sqrt(pow((c[a].x-c[b].x),2)+pow((c[a].y-c[b].y),2))<=D){ 53 int root1=find(a); 54 int root2=find(b); 55 c[root1].pre=root2; 56 } 57 } 58 int main() 59 { 60 while((N=read())){ 61 D=read(); 62 // memset(rep,0,sizeof(rep)); 63 for(int i=1;i<=N;i++){ 64 c[i].x=read(); 65 c[i].y=read(); 66 } 67 init();//初始化根 68 char str; 69 while(scanf("%c",&str)!=EOF){ 70 if(str=='O'){ 71 int a; 72 a=read(); 73 // rep[a]=1; 74 G.push_back(a); 75 for(int i=0;i<G.size();i++){ 76 // if(rep[i]==1){ 77 if(G[i]==a)//可有可无剪枝 78 continue; 79 union_find(G[i],a); 80 // } 81 } 82 } 83 if(str=='S'){ 84 int a,b; 85 a=read(); 86 b=read(); 87 if(find(a)==find(b)) 88 printf("SUCCESS\n"); 89 else 90 printf("FAIL\n"); 91 92 } 93 } 94 } 95 }
注意:最后判断博不可以 if(c[a].pre==c[b].pre) printf("SUCCESS\n"); ,pre只记录了前面的节点,并不是根,当然路径压缩记录了根,但 c[root1].pre=root2; 只是把某点加到某点下,如果没有再输入数据的话,那图其实并不是只有两层,除非再输入数据,经过find函数里走一遭才行,具体见此文,搜“用的测试数据”那个数据即可
反正我就感觉我这么费尽心血大费周章的搞一道题,下面很多题都会很容易A掉,而只背模板下面的题可能无法A出来,但我需要A好多题才能逐渐更加清晰每个细节,别人可能A一个题就懂了
###:付费编辑数学公式网站(文本方式存储),免费编辑公式网站(制作成图片插入页面)(来源于博客)
###:可以记录github修改过程的网站:GitHistory.xyz
###:看了下可用平台下面的“关于”,发现居然是一个团队,数据强度差这么多
###:无意间看到个有意思的:自动机?、vector、并查集变形?、链式向前星、dfs复杂度、谷雨C++、判断vector里是否有某元素、int初始化
###:

真tm锻炼心性艹,刚来图书馆爆满,md今天是世界读书日么哦周六,好不容易找到个位置对面做个长靴子黑丝,再对面坐着精致脸蛋的妹子。本来打算今天把题A掉的
###:关于ctrl+Z、ctrl+C,总结,深入解析: 001、002
###:PAT100分才有资格成为他徒弟的柳诺对我说:菜就菜,慢慢磨,总有一天会变强的。q神对我说:自己想破脑袋的怎么可能忘记。51nod也是
###:现在基本每天都自动黑屏提示3F0找不到硬盘,都要把电脑后面开瓢的地方掰一掰才能行了,哎~~~~(>_<)~~~~好心累
###:string类型不可以scanf,要么cin,要么先用char数组再转string
要么先分配空间(利用c_str()函数),要么
反正 string str; scanf("%s",str); 加不加 & 都不行
###:无意间发现的模板
###:注意此文中的快读有问题!!!:详见博客 (这句话是刷并查集题目时发现后更新的)
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· winform 绘制太阳,地球,月球 运作规律
· AI与.NET技术实操系列(五):向量存储与相似性搜索在 .NET 中的实现
· 超详细:普通电脑也行Windows部署deepseek R1训练数据并当服务器共享给他人
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 上周热点回顾(3.3-3.9)