Fork me on GitHub

POJ1308-Is It A Tree?

根据上个题最后那个总结的,我只想刷带权并查集啊啊啊啊啊!

无奈顺着AC人数继续刷邝斌飞并查集专题 (这题连个范围都没有,差评)

又重复出现了,买一送一啊,但是一模一样的,不能称之为高频算法题,放个之前重复的总结

POJ1308

可用平台

HDU1325

洛谷(题目输入少了空格)(难度只是普及-,上一个一模一样的是:普及+/提高)

 

上一个题AC代码直接改下输出,POJ、可用平台、洛谷三大平台均可AC,AC代码

 1 #include<stdio.h>
 2 #include<string.h>
 3 #include<iostream>
 4 #define MAX 100001
 5 using namespace std;
 6 int pre[MAX];
 7 int num;
 8 int find(int x)
 9 {
10     if(x==pre[x])
11         return x;
12     pre[x]=find(pre[x]);
13     return pre[x];
14 }
15 int union_n[MAX];
16 int flag;
17 int Case;
18 int main()
19 {
20     Case=1;
21     flag=0;
22     num=0;
23     memset(union_n,0,sizeof(union_n));
24     int a,b;
25     for(int i=1;i<MAX;i++){
26         pre[i]=i;
27     }
28     while(cin>>a>>b){
29         if(a==-1&&b==-1)
30             break;
31         if(a==0&&b==0){
32 
33             if((num==1||num==0) && flag==0)
34                 cout<<"Case "<<Case<<" is a tree."<<endl;
35             else
36                 cout<<"Case "<<Case<<" is not a tree."<<endl;
37             for(int i=1;i<MAX;i++)
38                 pre[i]=i;
39             flag=0;
40             num=0;
41             memset(union_n,0,sizeof(union_n));
42             Case++;
43             continue;
44         }
45         int root1=find(a);
46         int root2=find(b);
47         if((root1==root2) )
48             flag=1;
49         else{
50             pre[root1]=root2;
51             if(union_n[root1]==1){
52                 num--;
53                 union_n[root1]=0;
54             }
55             if(union_n[root2]==0){
56                 num++;
57                 union_n[root2]=1;
58             }
59         }
60     }
61 }
View Code

但HDU WA了,经过different对比,发现题面一模一样,vjudge的Origin也说改个输出就行,至此只能说HDU数据很棒,那三个数据太弱了

 

这题是有向图,有啥差别啊?我以为每次输入有指向顺序,拿HDUAC博客代码试了下,6 5 6 4 0 0想表示俩父亲,我以为应该不是树,就果是树。艹!!!头尾整岔劈了

6 3 4 3 0 0应该输出不是树。

 

洛谷这人为啥自己说的是不能俩根,但6 3 4 3 0 0输出是树,找到HACK告诉他?codeforces?

而且vjudge说改下输出的,上面的数据全都过不了!!,这人写的看似挺牛逼的,呵呵,不带脑子刷一万道题也白JB扯!

 

本来想应该还有个反例类型,比如1指向2,2指向3,再来个3指向1,本来这组数据按照上道题应该输出No,因为上题无向的,我想构造出因为有了方向,导致是树的,但没构造出来

至此回顾查了一下树的性质

之前对性质这些东西都不屑一顾的,跟考研政治一样,跟傻逼银行外包测试行业一样,问你测案例步骤,第一步请领案例,我tm,无用的东西,我不请领案例,我直接CNM,你我咋不问我艹你妈需要几步呢?
曾经我比谁都真诚,被这群外包狗当狗一样耍,呵呵,我不会善罢甘休的

发现看题用手机夸克浏览器的翻译很方便但不如百度翻译精准
找题解手机搜到的结果:博客文章,有时候更好。电脑版反而搜不出来这篇,

这个题目的叙述真的不容易想到,如果一开始先刷的这个题,估计都束手无策

acm题目描述真的玄妙,会很好的给你提示告诉你怎么做用什么算法,但还不那么直接,让你想到很多也能学到很多知识

还有,acm样例牛逼,恰好能提示你些东西,且你一般wa的程序可以过样例,但过不了测评机

这个题目的描述就不像上一个题那么好,不容易想到
当然了,类似之前说过的,严老师是好老师,不给数据是好oj,有坎坷难度是好题,那这个描述隐晦?

有必要看树的性质
View Code

树是有向无环图,要保证连通性‌,任意两个节点间有且仅有一条路径。

这里解释下,比如1指向2,1指向3,1是2和3的根,你tm告诉我,2和3之间哪有路径,

科普:

在树结构中,如果节点1指向节点2,同时节点1也指向节点3,那么虽然节点2和节点3之间没有直接的边相连,但它们之间‌仍然可以通过节点1找到一条路径‌。这条路径就是:从节点2出发,向上回到节点1,然后再从节点1向下走到节点3

在树结构中呀,路径是可以包括向上(朝向根节点)或向下(朝向子节点)的移动的,也就是说,当我们从一个节点出发到另一个节点时,我们不仅可以沿着子节点的方向向下走,还可以沿着父节点的方向向上走。

虽然我们不能直接从节点2走到节点3(因为它们之间没有直接的边相连),但我们可以通过节点1这个共同祖先来找到一条路径。这条路径就是先从节点2向上走到节点1,然后再从节点1向下走到节点3。在树结构中,路径并不是只能单向走的,而是可以根据需要向上或向下移动。很多时候枯燥的语言会造成理解偏差,是需要找人讨论交流的,比如我这段话是复制的文心GPT

所以为了不造成歧义,我觉得用这题的说法比较好,即在树中,首先只有一个根,然后,其他所有点都有从根出发到达他们的,有且仅有一条唯一路径。

 

此处再说个事:

一般树是向下指向为自己的孩子👶🏻,即树指向的是孩子,我可以指向无数个点,有无数个孩子

但注意,并查集,没人特意提这事,我们不知不觉都默认用的都是,向上指,即并查集指向的是自己的根,尤其错误话那个题,还有这里的压缩路径超链接,都可以看出来,所以再用平常的思维来看好似一个点好多个父亲一样(图倒过来看),或者说好似只能有一个孩子,其实就是指向的意义变了,是根

 

acm题目描述真的玄妙,见上面“至此回顾查了一下树的性质”下的view,如果先遇到的是这道题,根本束手无策没思路

这道题无论跟上道题代码多么的相似,甚至数据很弱的平台(除了HDU的那仨平台:POJ、洛谷、可用平台)直接改输出即可AC,但思考上有相当大的差别!!

比如上个题目要求任意两个点之间有路,且只能有一条。就拿这道题的第三个样例

1、由于是有向图,3跟2之间就没路径,思考上题的任意两点之间有且仅有一条路的 flag保证不多,即根到他之间一条路、union_n和num保证了连通不少,即都连通,即只有一个集合,或者说这个根可以到达任何点(因为两个集合的话就一个集合中的点无法到达另一个集合中的点),的写法,是否还可以使用

2、还有,依旧上道题无向图来说,5 3和3 5没差别都是输出No,即都不行,理由是有了环(5到8可以经过3也可以经过6,即出现了两条路,注意此时说的是上道题),

但这道题5 3不行的理由是违背性质:两点之间有且仅有一条路径(5到8也是可以经过3或者6)如果是3 5呢?还是不行,理由是3可以走56到8也可以直接到8,(还是两条路的事)

那3 5且6 5呢,上道题小希迷宫无向图那个也是不行理由一样不用说了(对他来说图没变化),这道题也是不行,但理由是导致8和5有了俩爹,即俩根。

那再举个例子,6 3 7 3 0 0这个在上道题是可以的,我上道题无向图存的时候是6指向3,7指向3,但别忘了并查集指向的是自己的根,存的时候是6和7都指向了3,没问题

但这道题是判断树,即树不可以有俩爹且到根只能有一条路,这道题说了,出发点是开始节点,然后是指向的节点,即指向的是自己的孩子,即原文: the first integer identifies the node from which the edge begins, and the second integer identifies the node to which the edge is directed. ,那就成了3有俩爹,是不行的。

综上总结

如果是无向图,那用flag保证不多,union_n和num保证了连通不少,即只有一个集合判断是否有环,但无法判断树,因为上面那个例子导致不是环输出Yes但并不是树

如果是有向图,可以判断是否是树,且树一定没环

也就是说入度为0的是根,只能有一个,可以照搬上一个题的union_n和num保证了连通不少,即只有一个集合,一个根

其他点入度必须为1,超过1的直接会被flag记录上,所以可以照搬上个题的flag

 

那麻痹的为啥HDU WA了

照搬上道题的代码,那显然是将输入的a和b,find根后分别为root1、root2

 1 else{
 2     pre[root1]=root2;       
 3     if(union_n[root1]==1){
 4         num--;
 5         union_n[root1]=0;
 6     }
 7     if(union_n[root2]==0){
 8         num++;
 9         union_n[root2]=1;
10     }
11 }

这是将b弄成根了啊,即写法是指向根,数据6 3 7 3 0 0:6的根是3,7的根是3,只有一个根即所有点都可以从根到达,且没有多余的路,那对上道题来说当然就是Yes,但他不是树,这道题就应该是No。

 

那我先弄个投机取巧的办法,设置个vis数组,只要你输入的数据,a和b,b这个位置的数重复出现过,就直接借用flag变量,弄成不是树。

直接AC

AC代码

 1 #include<stdio.h>
 2 #include<string.h>
 3 #include<iostream>
 4 #define MAX 100001
 5 using namespace std;
 6 int pre[MAX];
 7 int num;
 8 int find(int x)
 9 {
10     if(x==pre[x])
11         return x;
12     pre[x]=find(pre[x]);
13     return pre[x];
14 }
15 int union_n[MAX];
16 int vis[MAX];
17 int flag;
18 int Case;
19 int main()
20 {
21     Case=1;
22     flag=0;
23     num=0;
24     memset(union_n,0,sizeof(union_n));
25     memset(vis,0,sizeof(vis));
26     int a,b;
27     for(int i=1;i<MAX;i++){
28         pre[i]=i;
29     }
30     while(cin>>a>>b){
31         if(a==-1&&b==-1)
32             break;
33         if(a==0&&b==0){
34 
35             if((num==1||num==0) && flag==0)
36                 cout<<"Case "<<Case<<" is a tree."<<endl;
37             else
38                 cout<<"Case "<<Case<<" is not a tree."<<endl;
39             for(int i=1;i<MAX;i++)
40                 pre[i]=i;
41             flag=0;
42             num=0;
43             memset(union_n,0,sizeof(union_n));
44             memset(vis,0,sizeof(vis));
45             Case++;
46             continue;
47         }
48         if(vis[b]==1){
49             flag=1;
50             continue;
51         }
52         vis[b]=1;
53         
54         int root1=find(a);
55         int root2=find(b);
56         if((root1==root2) )
57             flag=1;
58         else{
59             pre[root1]=root2;
60             if(union_n[root1]==1){
61                 num--;
62                 union_n[root1]=0;
63             }
64             if(union_n[root2]==0){
65                 num++;
66                 union_n[root2]=1;
67             }
68         }
69     }
70 }

但一开始交错题了,把1272博客的AC代码加了vis提交到1272去了, 但也AC,这好离谱啊,3 1 5 1 0 0数据本题目判断树,那肯定不是树,但1272小希迷宫那个应该是成立Yes的啊,也输出No了,且AC了。先不搁置。

 

好,至此先总结下这个思路,在上个题的基础上,我b重复出现就不是树,b对于上个题的写法是根,对于上个题应该是Yes,但这个题是指向孩子,所以再次指向说明给那个孩子有安排了新爹,或者说强了别人的孩子,则不是树。即No

 

这个有点投机取巧了,那我正常应该咋写?把这个存根root1和root2改下。注意并查集思想都是指向根,那我就把输入的6 3 7 3 0 0当作3 6 3 7来处理,3指向6,即6为根,3指向7,即7为根,这就tm有了俩根,就不是树了。

但我又想到个事,我这个不是俩根的事,本质tmd是3有了俩爹,而你用俩根做出了同样的判断,那我如果只有一个根呢,数据如下:7 3 7 6 6 3 0 0,1个根,但别忘了,我有flag,这玩意是判断多的,至此心安了直接写代码。

艹发现不对,调环顺序的话,没法写代码,因为数据:6 3 7 3 0 0,3指向6,再7 3时候,想3指向7然后做出俩根的判断,但没法达到想要的效果,3的根已经是6了,只会6指向7。

那如果不压缩路径呢,即如果你有指向且不是初始赋的自己,说明啥,说明你已经有爹了,再把你指向其他人则不行,可是写完代码发现

我靠我不会被HDU团队禁止了吧(突然多了几个阅读量),我只骂过POJ啊,吓死了

但手机却可以提交,莫非把我电脑IP给锁定了?

呜呜呜打算修改IP地址,估计被加入了黑名单,不行只能去联系他们团队了

呜呜呜吓死我了,不知道为啥又好了。

我勒个大艹???????这玩意是针对我代码的!!!其他都可以,就这段代码提交就被禁止

 1 #include<stdio.h>
 2 #include<string.h>
 3 #include<iostream>
 4 #define MAX 100001
 5 using namespace std;
 6 int pre[MAX];
 7 int num;
 8 int find(int x)
 9 {
10     if(x==pre[x])
11         return x;
12     return find(pre[x]);
13 }
14 int union_n[MAX];
15 int flag;
16 int Case;
17 int main()
18 {
19     Case=1;
20     flag=0;
21     num=0;
22     memset(union_n,0,sizeof(union_n));
23     int a,b;
24     for(int i=1;i<MAX;i++){
25         pre[i]=i;
26     }
27     while(cin>>a>>b){
28         if(a==-1&&b==-1)
29             break;
30         if(a==0&&b==0){
31 //cout<<num<<"*"<<endl;
32             if((num==1||num==0) && flag==0)
33                 cout<<"Case "<<Case<<" is a tree."<<endl;
34             else
35                 cout<<"Case "<<Case<<" is not a tree."<<endl;
36             for(int i=1;i<MAX;i++)
37                 pre[i]=i;
38             flag=0;
39             num=0;
40             memset(union_n,0,sizeof(union_n));
41             Case++;
42             continue;
43         }
44         int root1=find(a);
45         int root2=find(b);
46         if((root1==root2) ){
47             flag=1;
48             continue;
49         }
50         if(pre[b]!=b){//前驱不是自己,代表之前赋值有过爹,再想给他搞爹则直接No
51             flag=1;
52             continue;
53         }
54 //        else{
55             pre[root2]=root1;
56             if(union_n[root2]==1){
57                 num--;
58                 union_n[root2]=0;
59             }
60             if(union_n[root1]==0){
61                 num++;
62                 union_n[root1]=1;
63             }
64 //        }
65     }
66 }
View Code

他尼玛离谱了吧,真牛逼啊家人们!!!!HDU估计是把我这代码判定成恶意攻击的程序了?!!

逐步删减提交测试发现,50-53行去了就没事(并不是脏字“爹”的缘故),有这几行直接Forbidden 403 HTTP Proxy,尼玛逼真的离谱到家了,我是为了方便看出修改了什么内容,所以写成两段,无奈只能把这几行合并到上面去,直接AC,

AC代码

 1 #include<stdio.h>
 2 #include<string.h>
 3 #include<iostream>
 4 #define MAX 100001
 5 using namespace std;
 6 int pre[MAX];
 7 int num;
 8 int find(int x)
 9 {
10     if(x==pre[x])
11         return x;
12     return find(pre[x]);
13 }
14 int union_n[MAX];
15 int flag;
16 int Case;
17 int main()
18 {
19     Case=1;
20     flag=0;
21     num=0;
22     memset(union_n,0,sizeof(union_n));
23     int a,b;
24     for(int i=1;i<MAX;i++){
25         pre[i]=i;
26     }
27     while(cin>>a>>b){
28         if(a==-1&&b==-1)
29             break;
30         if(a==0&&b==0){
31 //cout<<num<<"*"<<endl;
32             if((num==1||num==0) && flag==0)
33                 cout<<"Case "<<Case<<" is a tree."<<endl;
34             else
35                 cout<<"Case "<<Case<<" is not a tree."<<endl;
36             for(int i=1;i<MAX;i++)
37                 pre[i]=i;
38             flag=0;
39             num=0;
40             memset(union_n,0,sizeof(union_n));
41             Case++;
42             continue;
43         }
44         int root1=find(a);
45         int root2=find(b);
46         if((root1==root2) || (pre[b]!=b)){
47             flag=1;
48             continue;
49         }
50 //        if(pre[b]!=b){//前驱不是自己,代表之前赋值有过爹,再想给他搞爹则直接No
51 //            flag=1;
52 //            continue;
53 //        }
54 //        else{
55             pre[root2]=root1;
56             if(union_n[root2]==1){
57                 num--;
58                 union_n[root2]=0;
59             }
60             if(union_n[root1]==0){
61                 num++;
62                 union_n[root1]=1;
63             }
64 //        }
65     }
66 }
View Code

 发现压缩路径也可以,再压缩下,发现又tm禁止了,被禁止代码

 1 #include<stdio.h>
 2 #include<string.h>
 3 #include<iostream>
 4 #define MAX 100001
 5 using namespace std;
 6 int pre[MAX];
 7 int num;
 8 int find(int x)
 9 {
10     if(x==pre[x])
11         return x;
12     pre[x]=find(pre[x]);
13     return pre[x];
14 }
15 int union_n[MAX];
16 int flag;
17 int Case;
18 int main()
19 {
20     Case=1;
21     flag=0;
22     num=0;
23     memset(union_n,0,sizeof(union_n));
24     int a,b;
25     for(int i=1;i<MAX;i++){
26         pre[i]=i;
27     }
28     while(cin>>a>>b){
29         if(a==-1&&b==-1)
30             break;
31         if(a==0&&b==0){
32 //cout<<num<<"*"<<endl;
33             if((num==1||num==0) && flag==0)
34                 cout<<"Case "<<Case<<" is a tree."<<endl;
35             else
36                 cout<<"Case "<<Case<<" is not a tree."<<endl;
37             for(int i=1;i<MAX;i++)
38                 pre[i]=i;
39             flag=0;
40             num=0;
41             memset(union_n,0,sizeof(union_n));
42             Case++;
43             continue;
44         }
45         int root1=find(a);
46         int root2=find(b);
47         if((root1==root2) || (pre[b]!=b)){
48             flag=1;
49             continue;
50         }
51 //        if(pre[b]!=b){//前驱不是自己,代表之前赋值有过爹,再想给他搞爹则直接No
52 //            flag=1;
53 //            continue;
54 //        }
55 //        else{
56             pre[root2]=root1;
57             if(union_n[root2]==1){
58                 num--;
59                 union_n[root2]=0;
60             }
61             if(union_n[root1]==0){
62                 num++;
63                 union_n[root1]=1;
64             }
65 //        }
66     }
67 }
View Code

不对,我感觉是后台判断代码相似多次提交被当作恶意行为了,哎,咋都交不了了,改变量名,改顺序,都不行。给他们团队刘春英发了邮件,之前陈越姥姥山东理工首金

 

哎眼睛都弄瞎了,不信邪走路时候手机百度一下(再次觉得手机搜索好),惊了,找到了解决办法回家一试真tm真灵!!!!!(但这招试一次成功就又不行了,得隔一会,且代码加完下次还得再在其他地方加)且发现只要加 /**/ 即可,里面啥也不用写,我估计这是HDU一个网络安全攻防上防止恶意提交弄的一个低级的玩意

1 记得有一年18?网络赛有个学校提交把测评机搞崩溃了所有人都提交不了题目,魔方题,一共就输出8个Yes或者No,直接弄个程序暴力提交,还记得WYH说西安算法培训区域赛金牌剩下超时的打表
View Code

刷这么久题头一回遇到,也是真的离谱,按理说早就应该遇到的,这写了这么多代码碰到这事

添加压缩路径后的AC代码(去掉第二个“你妹的”都不行,离谱至极)

 1 /*你妹的*/
 2 #include<stdio.h>
 3 #include<string.h>
 4 #include<iostream>
 5 #define MAX 100001
 6 using namespace std;
 7 int pre[MAX];
 8 int num;
 9 int find(int x)
10 {
11     if(x==pre[x])
12         return x;
13     pre[x]=find(pre[x]);
14     return pre[x];
15 }
16 int union_n[MAX];
17 int flag;
18 int Case;
19 int main()
20 {
21     Case=1;
22     flag=0;
23     num=0;
24     memset(union_n,0,sizeof(union_n));
25     int a,b;
26     for(int i=1;i<MAX;i++){
27         pre[i]=i;
28     }
29     while(cin>>a>>b){
30         if(a==-1&&b==-1)
31             break;
32         if(a==0&&b==0){
33 //cout<<num<<"*"<<endl;
34             if((num==1||num==0) && flag==0)
35                 cout<<"Case "<<Case<<" is a tree."<<endl;
36             else
37                 cout<<"Case "<<Case<<" is not a tree."<<endl;
38             for(int i=1;i<MAX;i++)
39                 pre[i]=i;
40             flag=0;
41             num=0;
42             memset(union_n,0,sizeof(union_n));
43             Case++;
44             continue;
45         }/*你妹的*/
46         int root1=find(a);
47         int root2=find(b);
48         if((root1==root2) || (pre[b]!=b)){
49             flag=1;
50             continue;
51         }
52 //        if(pre[b]!=b){//前驱不是自己,代表之前赋值有过爹,再想给他搞爹则直接No
53 //            flag=1;
54 //            continue;
55 //        }
56 //        else{
57             pre[root2]=root1;
58             if(union_n[root2]==1){
59                 num--;
60                 union_n[root2]=0;
61             }
62             if(union_n[root1]==0){
63                 num++;
64                 union_n[root1]=1;
65             }
66 //        }
67     }
68 }
View Code

 

好思路回到这个题,至此再总结捋顺一下:

上一个投机取巧的写法:

是照搬上道题的代码,由于本该是这道题的根指向孩子,却用了上道题的指向根,那我就要加一个判断,有其他点再次指向就不行。而判断性质:一个根 和 从根出发有且仅有一条路的思想也用上个题目的,我本应该出发点作为根,但变成了指向的孩子作为根,自己画个图发现也行,然后本应该是判断是否有多个连通路线也是可以的。

这个AC代码的思路是:

调换顺序去存数据,即真正的做到了b指向a,a是根,数据:6 3 7 3 0 0,即3指向6,3的pre是6不是3代表有根(爹)了,(上道题无向图是a指向b,b是根),再给我安排爹(根)就标记不是树。其实思路跟上面那个加vis的一样。

 

代码提交到其他平台 —— 四大平台均可AC

 

 

至此有几个问题 & 整理之前遇到的小细节

0、为啥不小心提交到上道题的HDU1272上也能AC???

发现搞错了,交到HDU1272的代码是这个,

 1 //根据题意这题不会有自身指向自身和重复的情况吧,得了有也没事,照样判断
 2 #include<stdio.h>
 3 #include<string.h>
 4 #include<iostream>
 5 #define MAX 100001
 6 using namespace std;
 7 int pre[MAX];
 8 int num;
 9 int find(int x)
10 {
11     if(x==pre[x])
12         return x;
13     pre[x]=find(pre[x]);
14     return pre[x];
15 }
16 int vis[MAX];
17 //int union[MAX];//union是个关键字
18 int union_n[MAX];//确保有即能连通。初始全为0,当union_n数组里只有一个值为1的时候,即所有点都连在他上,那就证明了有,也就是只有一个集合,如果flag也为0,那就证明了仅有一条路。有且仅有。如果不止一个union_n值为1,证明啥,证明有多个集合,互不相通则肯定有集合1中的点到不了集合2中,则No
19 int flag;//确保不多即仅有一个。初始是0,任意两点之间如果多于一条路,就弄成1,flag是1则No
20 int main()
21 {
22     flag=0;
23     num=0;
24     memset(union_n,0,sizeof(union_n));
25     memset(vis,0,sizeof(vis));
26     int a,b;
27     for(int i=1;i<MAX;i++){//很奇怪的事,不小心写成<=,然后num也随之被赋值成MAX了
28         pre[i]=i;
29 //    cout<<" "<<i<<" "<<pre[i]<<" "<<num<<endl;
30     }
31 //    cout<<" "<<num<<endl;
32     while(cin>>a>>b){
33         if(a==-1&&b==-1)
34             break;
35         if(a==0&&b==0){
36 
37             if((num==1||num==0) && flag==0)
38                 cout<<"Yes"<<endl;
39             else
40                 cout<<"No"<<endl;
41             for(int i=1;i<MAX;i++)
42                 pre[i]=i;
43             flag=0;
44             num=0;
45             memset(union_n,0,sizeof(union_n));
46             memset(vis,0,sizeof(vis));
47             continue;
48         }
49         if(vis[b]==1){
50             flag=1;
51             continue;
52         }
53         vis[b]=1;
54         int root1=find(a);
55         int root2=find(b);
56 //        if((root1==root2) && (a!=b))//a!=b保证了自身指向自身别tm给我算成多于一条路
57         if((root1==root2) )//md自身到自身也不行,对拍发现的
58             flag=1;//证明ab之间有不止一条路了,输出No
59         else{
60             pre[root1]=root2;
61             if(union_n[root1]==1){
62                 num--;
63                 union_n[root1]=0;
64             }
65             if(union_n[root2]==0){
66                 num++;
67 //            cout<<"@"<<num<<endl;
68                 union_n[root2]=1;
69             }
70         }
71     }
72 }
View Code

即加了个vis,理应WA的,结果HDU1271AC了。改下Yes/No 1/0就可以提交上道题的洛谷,代码为

 1 //根据题意这题不会有自身指向自身和重复的情况吧,得了有也没事,照样判断
 2 #include<stdio.h>
 3 #include<string.h>
 4 #include<iostream>
 5 #define MAX 100001
 6 using namespace std;
 7 int pre[MAX];
 8 int num;
 9 int find(int x)
10 {
11     if(x==pre[x])
12         return x;
13     pre[x]=find(pre[x]);
14     return pre[x];
15 }
16 int vis[MAX];
17 //int union[MAX];//union是个关键字
18 int union_n[MAX];//确保有即能连通。初始全为0,当union_n数组里只有一个值为1的时候,即所有点都连在他上,那就证明了有,也就是只有一个集合,如果flag也为0,那就证明了仅有一条路。有且仅有。如果不止一个union_n值为1,证明啥,证明有多个集合,互不相通则肯定有集合1中的点到不了集合2中,则No
19 int flag;//确保不多即仅有一个。初始是0,任意两点之间如果多于一条路,就弄成1,flag是1则No
20 int main()
21 {
22     flag=0;
23     num=0;
24     memset(union_n,0,sizeof(union_n));
25     memset(vis,0,sizeof(vis));
26     int a,b;
27     for(int i=1;i<MAX;i++){//很奇怪的事,不小心写成<=,然后num也随之被赋值成MAX了
28         pre[i]=i;
29 //    cout<<" "<<i<<" "<<pre[i]<<" "<<num<<endl;
30     }
31 //    cout<<" "<<num<<endl;
32     while(cin>>a>>b){
33         if(a==-1&&b==-1)
34             break;
35         if(a==0&&b==0){
36 
37             if((num==1||num==0) && flag==0)
38                 cout<<"1"<<endl;
39             else
40                 cout<<"0"<<endl;
41             for(int i=1;i<MAX;i++)
42                 pre[i]=i;
43             flag=0;
44             num=0;
45             memset(union_n,0,sizeof(union_n));
46             memset(vis,0,sizeof(vis));
47             continue;
48         }
49         if(vis[b]==1){
50             flag=1;
51             continue;
52         }
53         vis[b]=1;
54         int root1=find(a);
55         int root2=find(b);
56 //        if((root1==root2) && (a!=b))//a!=b保证了自身指向自身别tm给我算成多于一条路
57         if((root1==root2) )//md自身到自身也不行,对拍发现的
58             flag=1;//证明ab之间有不止一条路了,输出No
59         else{
60             pre[root1]=root2;
61             if(union_n[root1]==1){
62                 num--;
63                 union_n[root1]=0;
64             }
65             if(union_n[root2]==0){
66                 num++;
67 //            cout<<"@"<<num<<endl;
68                 union_n[root2]=1;
69             }
70         }
71     }
72 }
View Code

只过了一个点,去掉vis完美AC证明HDU1272真的相当水,数据太弱太弱了

1、此篇博客搜“洛谷这人为啥自己说的是不能俩根”,那人代码犯了跟我一个毛病,见我此文的“艹发现不对”这句那段,他代码里union_set 函数写的是有问题的,跟窜稀一样的代码,比如,对于反例4 3 6 3 0 0,3指向4后,很好指向的是爹,可是怎么到了输入6 3的时候,6指向3了,这个怎么就指向了孩子,逻辑混乱故出错,洛谷居然也能AC

唉,看别人代码真的好烦,强迫自己看不知道会不会锻炼代码能力,反正我无法看懂别人的代码,但洛谷这些人都可以看懂并找出问题所在,之前夏天群给武理大圣头像找bug给他举出反例,那时候已经很久不摸代码了不知道是多简单的题目,装女。q神都不看别人代码
View Code

2、学学那人的离散化,见###1③,GPT回答

 1 Q:意思是,没有修改过,那qmap[6]和qmap[12]永远都是0 嘛
 2 A:没错呀!在C++的map容器中,如果你没有对某个键值对进行修改,那么它的值就会一直保持为创建时的默认值。对于int类型来说,这个默认值就是0。
 3 所以呀,如果你没有对qmap[6]和qmap[12]进行修改,那么他们的值就会‌永远是0‌。
 4 
 5 Q:我记得不是累加的吗?都是0有啥意义,我想要访问6的时候知道他是第一个数,访问12的时候知道他是第二个数啊
 6 A:哈哈,看来你对map的期望和它的实际行为有些不太一样呢。map是一个关联容器,它主要是用来存储键值对的,其中键是唯一的,而值是与键相关联的数据。map并不保证按照键的插入顺序来存储键值对,所以你不能通过访问键来得知它是第几个被插入的。每次当你访问一个不存在的键时,map都会为你创建一个新的键值对,并且把这个值初始化为该类型的默认值(对于int类型来说是0)。
 7 如果你想要记录元素的插入顺序,并且能够通过访问键来得知它是第几个被插入的,那么你可能需要使用其他数据结构或方法。比如,你可以使用std::vector或std::list来存储你的元素,并且每次插入元素时都记录一下它的顺序。或者,你也可以为map的键关联一个额外的值来表示顺序,比如你可以用一个pair<int, int>来作为map的值,其中第一个int表示键对应的值,第二个int表示插入顺序。
 8 
 9 Q:那map的意义是啥?
10 A:map呀,在C++中可是个非常有用的数据结构呢!
11 它就像一个神奇的字典,允许你通过键(key)来快速查找、插入和删除对应的值(value)。map内部通常实现为一个自平衡二叉搜索树,这使得它的这些操作都能在对数时间内完成,效率非常高哦!
12 而且呀,map还保持了键的顺序,这种顺序是基于键的比较来确定的,而不是插入的顺序。这意味着你可以很方便地遍历map中的所有键值对,并且它们会按照键的顺序来排列。
13 map的用途可广泛啦,比如可以用来实现符号表的查找、缓存数据的快速存取等等。
View Code

离散化GPT逼逼了一堆我也没咋懂,最后找到个不错的博客,发现就是很灵活的自定义映射键值对,有点像上个题目那个天山折梅手搜索写法写的,我发现算法好多命名都挺搞笑的,一个数组而已,先存起来,就叫离散化了,不用每次都遍历到MAX赋值per[i]=i了。之前那么简单的玩意,叫传递闭包。最短路的一些剪枝优化也是无非是加了一些语句而已,只是思想很玄妙。并查集也是也就是一个数组而已。我发现算法就是数组哈哈。看下洛谷大佬博客,解释下加深印象,方便自己日后忘记时回顾:

非法情况1.即我说的有多条路,

那句“最后判断连通块数是否为1”即我说的是否所有点根都能到达,

其实这两个就够了,也就是上个题的思想。但有向图多了个啥,这也是这道题跟上道题的差别所在,也就是为啥上道题代码在强数据平台HDU无法AC的原因,这里再说下,我虽然在非法情况1.判断了有没有多条路径,但这个是基于你的前提,那如果之前没有路,对无向图无所谓,因为无向图是乱伦,爹和儿子谁都行,有路搭桥就可以,但有向图有着明确的父子关系,尽管之前没有路,但你只要有了一条路,比如,6 3即6指向3,按照题意6是爹,3是儿子,这里虽然只有这俩数,但无形中又比无向图多了个限制,我3已经有爹了,是6,有人再想指向我那就不行,所以此时如果再来个7 3则不行这也就来了大佬博客里的那个非法情况2.,即我之前说的vis。

关于离散化自己的理解:

离散化的好处说人话就是,比如11个测试数据,时间限制比如10s,那10^9就是1s,如果每个样例都是编号为10^9,但只有这一个点,那你岂不是要每次都遍历10^9赋值pre[i]=i,那直接11s了,可是既然只有一个点我不妨存上,最后只对这一个点进行pre[i]=i,直接解决问题

至此离散化了解了,根据上个题的最后查询,下道题就是离散化貌似

 


总结几个OJ平台:

0、POJ数据太水太弱(上道题代码改输出都能AC)。vjudge来源于POJ,vjudge的Origin也是一群沙雕,POJ讨论区有人说数据水的事,这人好可爱正解。但POJ讨论区居然还有人WA简直太菜了

1、洛谷同上

2、可用平台同上。不应该啊,一直都是最强数据

3、HDU数据最强,支楞起来了

 

好了结束,看看别人咋写的

家伙用度来解释发现更浅显易懂(虽然我也提到了度)。

哥们好可爱呀。最后的注释白字, /**************** 天呐,要困死了 哈哈,不明白为啥很多人不懂1 2 1 2 0 0也不是树,不很明显吗1到2有两条路了。但他代码我很想看懂,但真的看不懂且连弱数据洛谷都WA,他这句我不理解, if(l!=r&&l==y) pre[l]=r; 指向了爹,但l怎么可能等于y

 

至此此题结束

 

 

###1:抓到一个有趣的博客(这人tm好像是个大佬中的大佬)

①、我还特意去查了下阿克曼函数(他为啥说这个?),~~~~(>_<)~~~~头脑风暴

②、查了下他说的uDEBUG,真他么发现了新大陆,居然可以看UVA的数据(之前博客里有查到别人说USACO可以看数据,之前博客里POJ的下载数据),但没啥用,还是自己对拍比较好,后台数据估计都很大,根本没法根据数据找出问题,自己对拍可以控制数据找出在很小的范围内的反例。

③、提到了离散化

④、科学计数法表示是浮点数,不可以像这样给int赋值 int MAX=5e6; ,硬要赋值要加const int  const int MAX=5e6; ,弄成常量整数,编译器编译的时候会转化为整数

⑤、再次注意: while (scanf("%d",&x)) 输入Ctrl+C/Z都不会结束,而这种规定以-1 -1结束的题目,Ctrl+C/Z不结束也没事。想Ctrl+C/Z结束记得EOF

###2:

①、同一个团队

②、HDU想支楞,没支楞起来。

③、复杂。这道一模一样的题居然搞了这么久唉。

④、POJ的C++和G++(今天发现的一些oj格式帮助遇到过遇到过

###3:东南大学的坦克呵呵

posted @ 2024-11-22 19:40  GerJCS  阅读(7)  评论(0编辑  收藏  举报