2019暑期牛客多校第三场
B题:Crazy Binary String
这个B题最长01子序列这个就很明显了
我们取0,1中最少的那个数的2倍,这样的话,显然是最优的 这个还是很好做的
但是我们对这个01均衡的串的话我们该怎么操作呢?
一个很naive的想法是这个串有什么性质呢: 0 1 的数量相同 ,那么 0 1就可以相互抵消 ,只要两个对于对应的奇数位和两个对应的偶数位中间的01能互相消掉的话
那么我们可以就可以算贡献,这个的话就随便搞搞就ok了
还有一钟想法是二分长度,然后check,这个是有问题的 比如说 11000011 这个的话8是可以,4是不行的
关于这个杨例怎么造呢,我也不知道啊????????????????????????????????
这个也没啥办法,只能靠自己领会了
下面是代码:
1 #include <cstdio> 2 #include <cstring> 3 #include <algorithm> 4 #include <iostream> 5 #include <cmath> 6 #include <bitset> 7 typedef long long ll; 8 using namespace std; 9 const int maxn=(int)(1e5+2000); 10 int n; 11 char s[maxn]; 12 int qilen[maxn*2],oulen[maxn*2]; 13 int main(){ 14 scanf("%d",&n); 15 scanf("%s",s+1); 16 memset(qilen,-1,sizeof(qilen)); 17 memset(oulen,-1,sizeof(oulen)); 18 int cnt0=0,cnt1=0; 19 int ans=0; 20 oulen[maxn]=0; 21 int q=maxn; 22 //cout<<q<<endl; 23 for(int i=1;i<=n;i++){ 24 if(s[i]=='0') cnt0++; 25 else cnt1++; 26 if(s[i]=='0') q--; 27 else q++; 28 // cout<<q<<endl; 29 if(i%2==1){ 30 if(qilen[q]==-1) qilen[q]=i; 31 else{ 32 ans=max(ans,(i-qilen[q])); 33 } 34 }else{ 35 if(oulen[q]==-1) oulen[q]=i; 36 else ans=max(ans,i-oulen[q]); 37 } 38 } 39 printf("%d %d\n",ans,2*min(cnt0,cnt1)); 40 return 0; 41 }
H题:Magic Line
这个题的话我们首先想一下怎么分成两半,一个最简单的想法是排序,排完序之后
因为是偶数个吗,中点自然就出现了
然后在中间的点把他们分开,具体用一个两个1e9,-1e9的点来分开就ok,这样还是挺好操作的。大概一下子就能搞掉
下面是代码:
1 #include <cstdio> 2 #include <cstring> 3 #include <algorithm> 4 #include <iostream> 5 #include <cmath> 6 #include <bitset> 7 typedef long long ll; 8 using namespace std; 9 const int maxn=1100; 10 const ll Max=(ll)(1e9); 11 int t,n; 12 struct point{ 13 ll xi,yi; 14 }; 15 point sum[maxn]; 16 17 bool cmp(point p1,point p2){ 18 if(p1.xi==p2.xi) return p1.yi<p2.yi; 19 else return p1.xi<p2.xi; 20 } 21 22 int main(){ 23 scanf("%d",&t); 24 while(t--){ 25 scanf("%d",&n); 26 for(int i=1;i<=n;i++) scanf("%lld%lld",&sum[i].xi,&sum[i].yi); 27 sort(sum+1,sum+n+1,cmp); 28 int m=n/2; 29 if(sum[m].xi==sum[m+1].xi){ 30 if((sum[m+1].yi-sum[m].yi)%2==1){ 31 printf("%lld %lld %lld %lld\n",sum[m].xi-1,(ll)(3e8)+sum[m+1].yi,sum[m].xi+1,sum[m].yi-(ll)(3e8)); 32 }else{ 33 ll y=(sum[m+1].yi+sum[m].yi)/2; 34 printf("%lld %lld %lld %lld\n",sum[m].xi-1,(ll)(3e8)+y,sum[m].xi+1,y-(ll)(3e8)); 35 } 36 }else{ 37 ll y=(sum[m].yi+sum[m+1].yi)/2; 38 printf("%lld %lld %lld %lld\n",sum[m].xi,y+(ll)(3e8),sum[m+1].xi,y-(ll)(3e8)); 39 } 40 } 41 return 0; 42 }
F题:Planting Trees
这个的话我们怎么搞呢?????
首先我们知道如果我们要得到最大的区间,在根据他给的数据,知道这个东西某种情况下是需要穷举的,这个的话是要靠我们来努力发现的。
我觉得每道acm题目都是一道道模拟题, 他会给你一个条件,然后我们根据这些条件,应用上来,用这些应用上来的东西发现一个一个的性质,最后
根据性质我们得出这个题目的做法,这个是一般的Acm题的做法
首先我们想看一个矩形怎么合格呢,就是这个矩形的最大值-最小值>m
按照一般的想法我们得枚举出矩形的上边界,下边界,左边界,右边界。然后根据这几个边界中的最大值-最小值>m来做这道题,但是这样就会出现一个问题。
我们这样搞虽然可以通过缓存 然后把题目的复杂度变成n^4来做,但是这个n^4的话显然是会超时的,所以面对这样的情况,我们需要更改一下子,我们枚举上下边界后
肯定可以知道每一列中那个对应的每一列的最大值和最小值,然后我们根据这个最大值,最小值来慢慢操作。
一个显然可以确定的一点是我们可以枚举右边界,然后左边届其实会随着右边界慢慢缩小的,其实这样的话我们只需要维护左边届,缩小的那个条件,我们就可以o(n)完成某个上边界和下边界的操作
没错,就是这样子弄的,我们于是可以枚举右边界,右边界不断向右移动,然后维护一个左边的指针,他一共移动n次。然后我们维护一个最大值单调递减的队列,最小值单调递增的队列。每次把当前边界入队
然后我们看一下最大值-最小值能不能满足条件,不能,l++,把不满足条件的出队即可。
注意一点,这道题常数很大,我们不能通过deque来做,否则会t,我们只能用手写的双端队列,并且当我们在做这道题的时候,还得注意一下
我一开始加了个不必要的特判,然后一直死t,最后删掉了特判,一下子就过了。
下面是代码:
1 #include <cstring> 2 #include <algorithm> 3 #include <iostream> 4 #include <cmath> 5 #include <bitset> 6 #include <cstdio> 7 #include <queue> 8 typedef long long ll; 9 using namespace std; 10 const int maxn=550; 11 int t,n,m,ans=0; 12 int num[maxn][maxn]; 13 int mx[maxn],mn[maxn]; 14 int que1[maxn*2],que2[maxn*2]; 15 16 void solve(){ 17 for(int i=1;i<=n;i++){ 18 for(int j=1;j<=n;j++){ mx[j]=0;mn[j]=(int)(1e9+7);} 19 for(int j=i;j<=n;j++){ 20 // 确定 起点,确定终点 21 for(int k=1;k<=n;k++){ 22 mx[k]=max(mx[k],num[j][k]); 23 mn[k]=min(mn[k],num[j][k]); 24 } 25 int l=1;//做边界 26 int head1,tail1,head2,tail2; head1=tail1=head2=tail2=1; 27 //维护一个最大值的双端队列 维护一个最小值的双端队列 28 for(int k=1;k<=n;k++){ 29 while(head1<tail1&&mx[que1[tail1]]<mx[k]){ 30 tail1--; 31 }que1[++tail1]=k; 32 while(head2<tail2&&mn[que2[tail2]]>mn[k]){ 33 //if(i==1&&j==1) cout<<"aakkkkk"<<endl; 34 tail2--; 35 } que2[++tail2]=k; 36 37 while(head1<tail1&&head2<tail2&&mx[que1[head1+1]]-mn[que2[head2+1]]>m){ 38 l++; 39 while(head1<tail1&&que1[head1+1]<l) head1++; 40 while(head2<tail2&&que2[head2+1]<l) head2++; 41 } 42 ans=max(ans,(k-l+1)*(j-i+1)); 43 } 44 // cout<<i<<" "<<j<<" "<<ans<<endl; 45 } 46 } 47 } 48 49 int main(){ 50 scanf("%d",&t); 51 while(t--){ 52 scanf("%d%d",&n,&m); 53 ans=0; 54 for(int i=1;i<=n;i++) 55 for(int j=1;j<=n;j++) 56 scanf("%d",&num[i][j]); 57 solve(); 58 printf("%d\n",ans); 59 } 60 return 0; 61 }