标签:isp scanf cstring return div math ddd count() eve
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
Boys win! Girls win! Girls win! Boys win! Girls win! Boys win! Boys win! Girls win! Girls win! Boys win! Girls win!
这道题我们首先想到的就是模拟,但是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,但这已经足以说明问题)
标签:isp scanf cstring return div math ddd count() eve
原文地址:https://www.cnblogs.com/liuchanglc/p/12686398.html