朋友HDU - 5963 (思维题) 三种方法

Posted liuchanglc

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了朋友HDU - 5963 (思维题) 三种方法相关的知识,希望对你有一定的参考价值。

传送门

题目描述

技术图片

 

 输入

技术图片

 

 输出

技术图片

 

 样例输入

技术图片
2
2 3
1 2 0
0 1
1 2 1 1
0 2
4 11
1 2 1
2 3 1
3 4 0
0 1
0 2
0 3
0 4
1 2 1 0
0 1
0 2
0 3
1 3 4 1
0 3
0 4
Sample Input

样例输出

技术图片
Boys win!
Girls win!
Girls win!
Boys win!
Girls win!
Boys win!
Boys win!
Girls win!
Girls win!
Boys win!
Girls win! 
Sample Output

分析

这道题我们首先想到的就是模拟,但是40000的数据显然是太大了,肯定会超时

那么我们来模拟一下第一个样例

技术图片

 

 这是刚开始建好的边,建完边后我们发现这棵树没有能够修改的节点

所以我们对于第一个询问0 1显然要输出 Boys win!

接下来是一个修改边的的操作 1 2 1 1

修改完后就变成了下面这样

技术图片

 

 接下来又是一个询问操作0 2

我们发现在girls把(1,2)的边权修改为0后,boys不能再进行操作

所以很显然 Girls win!

第一个样例我们的模拟就结束了

是不是什么规律也没有看出来的,没有关系,我们再来第二组

(提示:注意观察与根节点相邻的边)

技术图片

 

 首先上来的就是四个询问,分别是1、2、3、4节点作为根节点

当1作为根节点时,操作如下图

技术图片

 我们发现,与根节点相邻的边的权值一开始为1,经过一次操作后变成了0,这时操作结束 Girls win! 

当2为根节点时

技术图片

我们发现,与根节点相邻的边有两个,权值一开始都为1,经过两次次操作后变成了0,这时操作结束 Boys win!

当3为根节点时

技术图片

 我们发现,与根节点相邻的边有两个,一个为1,一个为0,经过一次操作后1的那个变成了0,这时操作结束 Girls win!

当4为根节点时(画图好难用

技术图片

 

 

 我们发现,与根节点相邻的边的权值一开始为0,经过两次操作后从0变为1又变为0,这时操作结束 Boys win!

下面是一个修改边权的操作 1 2 1 0

修改完后,就成了这样

当1为根节点时

技术图片

 

  我们发现,与根节点相邻的边的权值一开始为0,经过两次操作后从0变为1又变为0,这时操作结束 Boys win!

当2为根节点时

技术图片

 我们发现,与根节点相邻的边有两个,一个为1,一个为0,经过一次操作后1的那个变成了0,这时操作结束 Girls win!

当3为根节点时

技术图片

我们发现,与根节点相邻的边有两个,一个为1,一个为0,经过一次操作后1的那个变成了0,这时操作结束 Girls win!

最后又是一个修改边权的操作,我们就不再模拟

通过以上的模拟,我们可以发现什么呢?

1、操作奇数次,girls win,操作偶数次boys win(是不是很显然

2、如果根节点只有一条边相连,那么如果这条边的边权为1,需要操作奇数次才能把它变成0,因为你的每一次操作都会对它产生影响,而且你无论后面操作多少次,最终还是要把它变为0,根据第一条性质,girls win

如果边权是0呢,就和上面相反,boys win

3、如果有多条边呢,我们就把每一条边上的操作次数累加,再根据性质1判断

方法一

听到这里,你是不是很激动呢,当给出一个根节点时,我们只需要把与它相邻的边的边权加和,再判断奇偶性就可以了

这里要注意的是,修改边的操作不一定修改成与原来相反的价值,有可能原来价值为1,修改后还为1

代码

技术图片
 1 #include<cstdio>
 2 #include<cstring>
 3 #include<algorithm>
 4 #include<iostream>
 5 #include<cmath>
 6 #include<ctime>
 7 using namespace std;
 8 const int maxn=80010;
 9 struct asd{
10     int from,to,next,val;
11 }b[maxn];
12 int head[maxn],tot=1;
13 void ad(int aa,int bb,int cc){
14     b[tot].from=aa;
15     b[tot].to=bb;
16     b[tot].next=head[aa];
17     b[tot].val=cc;
18     head[aa]=tot++;
19 }
20 int main(){
21     clock_t st, ed;
22     st = clock();
23     freopen("data.in","r",stdin);    
24     freopen("solution2.out","w",stdout);
25     int t;
26     scanf("%d",&t);
27     while(t--){
28         memset(head,-1,sizeof(head));
29         memset(&b,0,sizeof(struct asd));
30         tot=1;
31         int n,m;
32         scanf("%d%d",&n,&m);
33         for(int i=1;i<n;i++){
34             int aa,bb,cc;
35             scanf("%d%d%d",&aa,&bb,&cc);
36             ad(aa,bb,cc);
37             ad(bb,aa,cc);
38         }
39         while(m--){
40             int cc;
41             scanf("%d",&cc);
42             if(cc==0){
43                 int aa;
44                 scanf("%d",&aa);
45                 int ans=0;
46                 for(int i=head[aa];i!=-1;i=b[i].next){
47                     ans+=b[i].val;
48                 }
49                 if(ans%2==0) printf("Boys win!
");
50                 else printf("Girls win!
");
51             } else {
52                 int aa,bb,cc;
53                 scanf("%d%d%d",&aa,&bb,&cc);
54                 for(int i=head[aa];i!=-1;i=b[i].next){
55                     int u=b[i].to;
56                     if(bb==u && b[i].val!=cc){
57                         b[i].val=cc;
58                         break;
59                     }
60                 }
61                 for(int i=head[bb];i!=-1;i=b[i].next){
62                     int u=b[i].to;
63                     if(aa==u && b[i].val!=cc){
64                         b[i].val=cc;
65                         break;
66                     }
67                 }
68             }
69         }
70     }
71     ed=clock();
72     printf("Accepted. Time: %dms.
", int(ed - st));
73     return 0;
74 }
View Code

写完后,我们把它交上去,发现过了,时间消耗还不多

技术图片

 

 但是我们细细一想会发现,这种做法的时间效率不能保证,我们完全可以造一组数据将它卡成n^2

比如下面这样

技术图片

 

 m,n小于40000,我们完全可以按照上面那样建边,然后来39999次修改操作

最后再来一次询问

而且题目中最多会给出5组数据

那么耗时就是5*40000*40000,显然会T(后面会有样例,大家可以试一下)

方法二

既然如此,那我们就要考虑怎么省去遍历边的操作

题目中只给出了0,1两种值

所以,联系我们最近学过的内容

没错,就是bitset

代码

技术图片
 1 #include<cstdio>
 2 #include<cstring>
 3 #include<algorithm>
 4 #include<iostream>
 5 #include<cmath>
 6 #include<bitset>
 7 #include<ctime>
 8 using namespace std;
 9 bitset<23005> bit[23005];
10 int main(){
11     clock_t st, ed;
12     st = clock();
13     freopen("data.in","r",stdin);    
14     freopen("solution1.out","w",stdout);
15     int t;
16     scanf("%d",&t);
17     while(t--){
18         for(int i=0;i<23000;i++){
19             bit[i].reset();
20         }
21         int n,m;
22         scanf("%d%d",&n,&m);
23         for(int i=1;i<n;i++){
24             int aa,bb,cc;
25             scanf("%d%d%d",&aa,&bb,&cc);
26             if(cc==1) bit[aa][bb]=bit[bb][aa]=1;
27         }
28         while(m--){
29             int cc;
30             scanf("%d",&cc);
31             if(cc==0){
32                 int aa;
33                 scanf("%d",&aa);
34                 int ans=0;
35                 ans+=(int)bit[aa].count();
36                 if(ans%2==0) printf("Boys win!
");
37                 else printf("Girls win!
");
38             } else {
39                 int aa,bb,cc;
40                 scanf("%d%d%d",&aa,&bb,&cc);
41                 if(cc==1) bit[aa][bb]=bit[bb][aa]=1;
42                 else bit[aa][bb]=bit[bb][aa]=0;
43             }
44         }
45     }
46     ed=clock();
47     printf("Accepted. Time: %dms.
", int(ed - st));
48     return 0;
49 }
View Code

但是很遗憾内存开不下

虽然bitset很优秀,只占一个二进制位,但是题目中的内存限制为65536 kB

最多可以开一维的bitset数组65536*1024*8=536870912(5亿多,是不是很强大)

但因为是二维数组,我们开方后就只有23000了,只能达到原题数据的一半左右

如果我们开40000*40000显然会M掉

如果开23000*23000呢,会RE,因为下标访问bitset数组并不会检查越界

方法三

这时,优秀的解法该出现了

是什么呢?

答案就是map+pair

map的用法大家应该都很熟悉了,我们就简单讲一下pair吧

摘自百度百科:

 定义:c++中的结构模板,定义在头文件<utility>中,提供一个包含2个数据成员的结构体模板。继承与_Pair_base结构体模板。通过first,second访问2个成员,有 operator= 和 swap 方法。

技术图片

 

 技术图片

 

 技术图片

 

以下内容摘自:https://blog.csdn.net/qq_42232118/article/details/82078854

技术图片

 

 技术图片

 

 技术图片

 

 技术图片

 

 技术图片

 

 技术图片

 

 其实,这里pair的作用就是把两个元素整合在一起

 代码

技术图片
 1 #include <stdio.h>
 2 #include <string.h>
 3 #include<map>
 4 #include<utility>
 5 #include<ctime>
 6 using namespace std;
 7 int deg[40005];
 8 map<pair<int,int>,int> amap;
 9 int main(){
10     clock_t st, ed;
11     st = clock();
12     freopen("data.in","r",stdin);    
13     freopen("solution3.out","w",stdout);
14     int t,n,m,x,y,z,id,j;
15     scanf("%d",&t);
16     while(t--){
17         memset(deg,0,sizeof(deg));
18         amap.clear();
19         scanf("%d%d",&n,&m);
20         for(int i=1;i<n;i++){
21             scanf("%d%d%d",&x,&y,&z);
22             if(x>y){int te=x;x=y;y=te;}  //统一顺序这样方便后期查找
23             amap[make_pair(x,y)]=z;
24             if(z==1){
25                 deg[x]++;
26                 deg[y]++;
27             }
28         }
29         for(int i=0;i<m;i++){
30             scanf("%d",&id);
31             if(id==0){
32                 scanf("%d",&x);
33                 if(deg[x]%2) printf("Girls win!
");
34                 else printf("Boys win!
");
35             }
36             else{
37                 scanf("%d%d%d",&x,&y,&z);
38                 if(x>y){int te=x;x=y;y=te;}
39                 if(amap[make_pair(x,y)]!=z){
40                     if(z==0){
41                         amap[make_pair(x,y)]=0;
42                         deg[x]--;
43                         deg[y]--;
44                     }
45                     else{
46                         amap[make_pair(x,y)]=1;
47                         deg[x]++;
48                         deg[y]++;
49                     }
50                 }
51             }
52         }
53     }
54     ed=clock();
55     printf("Accepted. Time: %dms.
", int(ed - st));
56     return 0;
57 }
View Code

比较

这是一组符合题目要求的样例

技术图片

 

 样例太大,插不上,就放一个生成数据的代码吧

技术图片
 1 #include<bits/stdc++.h>
 2 using namespace std;
 3 int main(){
 4     freopen("data.in","w",stdout);
 5     srand(time(NULL));
 6     printf("1
18000
37999
");
 7    for(int i=2;i<=18000;i++){
 8         printf("1 %d %d
",i,i%2);
 9     }
10     for(int i=1;i<=17999;i++){
11         printf("1 1 %d %d
",i,i%2+1);
12     }
13     for(int i=0;i<=10000;i++){
14       printf("0 %d
",i);
15     }
16     for(int i=0;i<=10000;i++){
17       printf("0 %d
",i);
18     }
19     return 0;
20 }
View Code

这是用bitset的耗时

技术图片

 

 这是用map+pair的耗时

技术图片

 

 这是直接枚举的耗时

技术图片

 

 (这里为了关照一下bitset,只用了20000的边,没敢开40000,但这已经足以说明问题)

以上是关于朋友HDU - 5963 (思维题) 三种方法的主要内容,如果未能解决你的问题,请参考以下文章

hdu 5963 朋友(2016ccpc 合肥站 C题)

[HDU5963]朋友

HDU 5963 朋友(找规律博弈)

HDU 5963 博弈

HDU 5963 朋友 博弈论 (中国大学生程序设计竞赛(合肥))

最简单的博弈论——HDU - 5963 朋友 (博弈)