9.6题解
T1
这题大家都会做,$gcd(n,m)=1$就可以让每个人都扔一次西瓜,不是一就不可以,关键在于高精度的灵活运用,这题可以打高精取模,但是我不会,所以我们选择二进制下计算$gcd$,大致流程如下
1.$n{\%}2==0$,$m{\%}2==0$,对于这道不用实际求出gcd的题来说,直接$return$ $false$就可以了
2.$n{\%}2==1$或$m{\%}2==1$,那就高精除低精,给偶数除2,直至偶数变成奇数
3.$n{\%}2==1$且$m{\%}2==1$,留下较小的数,高精减高精,大数减小数,返回第二步
边界条件就是一个数变成了1或0,变成一gcd肯定是1,变成0的话,gcd取决于另一个数
由于不断处以二,可以保证复杂度在$log$级别
1 #include<iostream> 2 #include<cstring> 3 #include<string> 4 #include<cstdio> 5 #define maxx 110 6 using namespace std; 7 int t; 8 struct node{ 9 int a[maxx],len; 10 }nn,mm; 11 char a[maxx],b[maxx]; 12 bool cmp(node x,node y)//1:x>=y 0:x<y 13 { 14 if(x.len>y.len) return 1; 15 else if(x.len<y.len) return 0; 16 else 17 { 18 for(int i=x.len;i>=1;--i) 19 { 20 if(x.a[i]>y.a[i]) return 1; 21 else if(x.a[i]<y.a[i]) return 0; 22 } 23 } 24 return 1; 25 } 26 node gjj(node x,node y)//x-y 27 { 28 for(int i=1;i<=y.len;++i) 29 { 30 if(x.a[i]-y.a[i]>=0) x.a[i]-=y.a[i]; 31 else {x.a[i+1]--; x.a[i]+=10; x.a[i]-=y.a[i];} 32 } 33 while(x.a[x.len]==0&&x.len!=1) x.len--; 34 return x; 35 } 36 node gjc(node x) 37 { 38 int carry=0; 39 for(int i=x.len;i>=1;--i) 40 { 41 int tmp=x.a[i]; 42 x.a[i]=(carry+x.a[i])/2; 43 carry=(carry+tmp)%2; carry*=10; 44 } 45 while(x.a[x.len]==0&&x.len!=1) x.len--; 46 return x; 47 } 48 void out(node x) 49 { 50 for(int i=x.len;i>=1;--i) cout<<x.a[i]; 51 cout<<" "; 52 } 53 bool check() 54 { 55 if(nn.len==1&&nn.a[nn.len]==1) {printf("Yes\n"); return 1;} 56 if(mm.len==1&&mm.a[mm.len]==1) {printf("Yes\n"); return 1;} 57 if(nn.len==1&&nn.a[1]==0) 58 { 59 if(mm.len==1&&mm.a[1]==1) {printf("Yes\n"); return 1;} 60 else {printf("No\n"); return 1;} 61 } 62 if(mm.len==1&&mm.a[1]==0) 63 { 64 if(mm.len==1&&mm.a[1]==1) {printf("Yes\n"); return 1;} 65 else {printf("No\n"); return 1;} 66 } 67 return 0; 68 } 69 int main() 70 { 71 scanf("%d",&t); 72 while(t--) 73 { 74 scanf("%s%s",a+1,b+1); 75 nn.len=strlen(a+1); mm.len=strlen(b+1); 76 for(int i=1;i<=nn.len;++i) nn.a[nn.len-i+1]=a[i]-'0'; 77 for(int i=1;i<=mm.len;++i) mm.a[mm.len-i+1]=b[i]-'0'; 78 if(a[nn.len]%2==0&&b[mm.len]%2==0) {printf("No\n"); continue;} 79 while(1) 80 { 81 if(check()) break; 82 while(nn.a[1]%2==0) nn=gjc(nn); 83 if(check()) break; 84 while(mm.a[1]%2==0) mm=gjc(mm); 85 if(check()) break; 86 if(!cmp(nn,mm)) swap(nn,mm); 87 nn=gjj(nn,mm); 88 } 89 } 90 return 0; 91 }
T2
考虑一种简单的情况,如果序列里只有0和1,那我们把0看作-1,1看作+1,合法序列的区间和肯定且必须就是0,那如果我们记录前缀和,前缀和相等的两个右端点之间的区间就是合法区间
如果加进2来呢,考虑容斥,不合法的情况只有3种,区间里0超过一半,1超过一半,2超过一半,假设我们求0超过一半的,那么1,2都同理,求0超过一半的,我们把0看作-1,1和2都看作+1,那要找的非法区间就是区间和小于0的,转化到前缀和上,其实就是形成逆序对的前缀和,树状数组就可以搞定
1 #include<iostream> 2 #include<cstring> 3 #include<cstdio> 4 #define ll long long 5 #define maxn 5001000 6 using namespace std; 7 int n; 8 ll ans,tot; 9 int a[maxn],c1[maxn*3],c2[maxn*3],c3[maxn*3]; 10 int qz[maxn][3]; 11 char s[maxn]; 12 int lowbit(int x) 13 { 14 return x&(-x); 15 } 16 void add(int c[],int pos) 17 { 18 for(;pos<=3*n+1;pos+=lowbit(pos)) c[pos]++; 19 } 20 ll query(int c[],int pos) 21 { 22 ll ans=0; 23 for(;pos>0;pos-=lowbit(pos)) ans+=1ll*c[pos]; 24 return ans; 25 } 26 int main() 27 { 28 scanf("%d",&n); scanf("%s",s+1); 29 for(int i=1;i<=n;++i) 30 { 31 for(int j=0;j<=2;++j) qz[i][j]=qz[i-1][j]; 32 a[i]=s[i]-'0'; qz[i][a[i]]++; 33 } 34 for(int i=1;i<=n;++i) 35 { 36 37 add(c1,qz[i-1][0]*2-i+n+2); tot+=query(c1,qz[i][0]*2-i+n); 38 add(c2,qz[i-1][1]*2-i+n+2); tot+=query(c2,qz[i][1]*2-i+n); 39 add(c3,qz[i-1][2]*2-i+n+2); tot+=query(c3,qz[i][2]*2-i+n); 40 } 41 ans=1ll*n*(n+1)/2; ans-=tot; 42 printf("%lld\n",ans); 43 return 0; 44 }
T3
期望题,又一次咕咕咕