Fork me on GitHub

POJ2236-Wireless Network

开始刷并查集,不打算刷多,就大概AC人数300以上的题吧(时间不咋多了,其他以后再说),数一巨巨acm路线

邝斌飞并查集

POJ 10000MS、65536K —— 这题10s

可用平台

 

名字叫无线网络,想起之前刷过的题名一样但不是一个题,之前自己写的东西太稚嫩了傻逼一样。这道题让我想到了之前刷的拓扑排序奶牛比赛题目(脑子啥也想不起来了,AC这道题后再回顾奶牛比赛吧)

既然到了并查集想下之前学过的东西,搜索肯定没啥思路,那最短路,题目说不会超过3*105行,总共1001个点,那最多会有3*105-1001行的操作让你判断。

而每次判断,点数N是1001个,那最多:fraction numerator left parenthesis 首项 1 plus 尾项 left parenthesis N minus 1 right parenthesis right parenthesis asterisk times 项数 left parenthesis N minus 1 right parenthesis over denominator 2 end fraction 条边,用堆优化迪杰斯特拉,只不过做个改动,松弛更新加入队列的时候仅仅是小于距离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 }
View Code
复制代码

简洁版:

复制代码
 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 }
View Code
复制代码

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 }
View Code
复制代码

生成数据:

复制代码
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 }
View Code
复制代码

画出图来发现死循环了了,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 }
View Code
复制代码

最后感觉,根本不用判断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
View Code
复制代码

发现还不行,比如这么个反例:

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
View Code
复制代码

 用的测试数据:(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 }
View Code
复制代码

(注:上面代码无法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 }
View Code
复制代码

而且还有个事把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 }
View Code
复制代码

 

注意:最后判断博不可以  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掉的
View Code

###:关于ctrl+Zctrl+C总结,深入解析: 001002

###:PAT100分才有资格成为他徒弟的柳诺对我说:菜就菜,慢慢磨,总有一天会变强的。q神对我说:自己想破脑袋的怎么可能忘记。51nod也是

###:现在基本每天都自动黑屏提示3F0找不到硬盘,都要把电脑后面开瓢的地方掰一掰才能行了,哎~~~~(>_<)~~~~好心累

###:string类型不可以scanf,要么cin,要么先用char数组再转string

要么先分配空间(利用c_str()函数),要么

 反正 string str; scanf("%s",str); 加不加 & 都不行

###:无意间发现的模板

###:注意此文中的快读有问题!!!:详见博客 (这句话是刷并查集题目时发现后更新的)

posted @   GerJCS  阅读(10)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· winform 绘制太阳,地球,月球 运作规律
· AI与.NET技术实操系列(五):向量存储与相似性搜索在 .NET 中的实现
· 超详细:普通电脑也行Windows部署deepseek R1训练数据并当服务器共享给他人
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 上周热点回顾(3.3-3.9)
点击右上角即可分享
微信分享提示