朋友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!\n");
50                 else printf("Girls win!\n");
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.\n", int(ed - st));
73     return 0;
74 }

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

但是我们细细一想会发现,这种做法的时间效率不能保证,我们完全可以造一组数据将它卡成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!\n");
37                 else printf("Girls win!\n");
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.\n", int(ed - st));
48     return 0;
49 }

但是很遗憾内存开不下

虽然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!\n");
34                 else printf("Boys win!\n");
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.\n", int(ed - st));
56     return 0;
57 }

比较

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

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

 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\n18000\n37999\n");
 7    for(int i=2;i<=18000;i++){
 8         printf("1 %d %d\n",i,i%2);
 9     }
10     for(int i=1;i<=17999;i++){
11         printf("1 1 %d %d\n",i,i%2+1);
12     }
13     for(int i=0;i<=10000;i++){
14       printf("0 %d\n",i);
15     }
16     for(int i=0;i<=10000;i++){
17       printf("0 %d\n",i);
18     }
19     return 0;
20 }

这是用bitset的耗时

这是用map+pair的耗时

这是直接枚举的耗时

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

原文地址:https://www.cnblogs.com/liuchanglc/p/12686398.html

时间: 2024-08-28 17:56:09

朋友HDU - 5963 (思维题) 三种方法的相关文章

win7系统升级到win10的三种方法win10专业版官网

win10系统,可以说是Windows系统中具有历史意义的一个系统,为什么这么说呢?想必很多朋友已经知道了吧,咱们win10系统将作为微软推出的最后一个Windows系统,下一代Windows将作为Update[1] 形式出现. 相信不少Win7/Win8.1的正版党都已经摩拳擦掌等待着免费升级Win10系统,然而,关于升级的细节可能多数玩家都不太清楚.现在,小编就给大家普及一下,如果你想免费升级Win10的三种方法. 值得一提的是,虽然明天7月29日才是Win10的真正发布日期,但由于Win1

asp.net跳转页面的三种方法比较

目前,对于学习asp.net的很多朋友来讲,实现跳转页面的方法还不是很了解.本文将为朋友们介绍利用asp.net跳转页面的三种方法,并对其之间的形式进行比较,希望能够对朋友们有所帮助. ASP.NET发展起源 1 ASP.NET的前身ASP技术,是在IIS 2.0上首次推出(Windows NT 3.51),当时与 ADO 1.0 一起推出,在IIS 3.0 (Windows NT 4.0)发扬光大,成为服务器端应用程序的热门开发工具,微软还特别为它量身打造了Visual InterDev开发工

Struts学习笔记(三)struts2中获得request、response和session的三种方法

struts2中获得request.response和session的三种方法   (1)非IoC方式 方法一:使用org.apache.struts2.ActionContext类,通过它的静态方法getContext()获取当前Action的上下文对象. ActionContext ctx = ActionContext.getContext(); ctx.put("liuwei", "andy"); //request.setAttribute("l

oracle__删除重复记录__三种方法及总结(转载百度文库)

http://wenku.baidu.com/link?url=RIENeGUK4sjxe21_RBYLYHR9tbUUCmOZQRR0mIjldXLYwRAt4khDtLQD9dFyd3rz3s_HWLvG2oErTw8sJUb1R2QLQqSZaBO3xLA8tu2qd9q --方法1:rowid --显示重复的行 select * from persons p1 where rowid<> (select max(rowid) from persons p2 where p1.pid=p

PHP实现链式操作的三种方法详解

这篇文章主要介绍了PHP实现链式操作的三种方法,结合实例形式分析了php链式操作的相关实现技巧与使用注意事项,需要的朋友可以参考下 本文实例讲述了PHP实现链式操作的三种方法.分享给大家供大家参考,具体如下: 在php中有很多字符串函数,例如要先过滤字符串收尾的空格,再求出其长度,一般的写法是: strlen(trim($str)) 如果要实现类似js中的链式操作,比如像下面这样应该怎么写? $str->trim()->strlen() 下面分别用三种方式来实现: 方法一.使用魔法函数__ca

三种方法实现Linux系统调用

这篇文章主要介绍了三种方法实现Linux系统调用,感兴趣的朋友可以参考一下系统调用(System Call)是操作系统为在用户态运行的进程与硬件设备(如CPU.磁盘.打印机等)进行交互提供的一组接口.当用户进程需要发生系统调用时,CPU 通过软中断切换到内核态开始执行内核系统调用函数.下面介绍Linux 下三种发生系统调用的方法: 一.通过 glibc 提供的库函数glibc 是 Linux 下使用的开源的标准 C 库,它是 GNU 发布的 libc 库,即运行时库.glibc 为程序员提供丰富

[Excel VBA]自定义排序的三种方法

诸君好,前前期我们聊了VBA编程和数据的常规排序……VBA常用小代码105:Rang对象的排序操作……今天我们再聊下自定义排序……何谓自定义排序,就是按指定的顺序对数据源进行排序呗…… 今一共分享了三种方法.第1种方法是系统自带的OrderCustom,优点是代码简洁,缺点是自定义序列有字符长度限制(255个).第2种方法是字典+数组设置序列号,再使用了辅助列进行排序.优点是不会破坏单元格的形式和结构,比如单元格中存在的公式.背景等.第3种方法是只使用字典+数组,借助简单桶排序的技巧,直接对数据

分区自动挂载的三种方法

一.修改/etc/rc.local配置文件追加类似命令:mount /dev/sda1  /sda1 到配置文件最后一行 二,修改/etc/fstab配置文件追加类似命令:/dev/sda1   /sda1  ext4   defaults   0  0 到配置文件中 三,安装autofs服务yum install autofs 追加类似命令:sda1   -fstype=ext4  :/dev/sda1 到配置/etc/autofs.misc文件中重启服务: service autofs re

VMWARE虚拟机不能上网三种方法分析

bridged(桥接模式). NAT(网络地址转换模式) host-only(主机模式). 理论理解: 1.bridged(桥接模式) 在这处模式下,虚拟机等同于网络内的一台物理主机,可对手动设置IP,子网掩码,DNS,且IP地址要和主机的IP在同一网段内.这样,虚拟机就和主机如同连在一个HUB上的两台计算机,只要主机能上网,虚拟机也会在这种模式下上网. 2.host-only(主机模式) 在host-only模式中,所有的虚拟系统是可以相互通信的,但虚拟系统和真实的网络是被隔离开的. 提示:在