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 }
View Code

 

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 }
View Code

 

 

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 }
View Code

 

    

posted on 2019-12-28 17:39  pandaking  阅读(186)  评论(0编辑  收藏  举报

导航