2019牛客多校訓練營(三)

B.Crazy Binary String

題目

給定一個長度爲n的只包含0和1的字符串。
要求0和1個數相等的最長的子序列和子串。

題解

子序列很簡單,統計全部的0和1的個數,它們兩者中的較小值×2即爲答案。
子串需要用sum數組記錄。其中sum[i]表示1~i中0和1數量的差值,如果存在sum[i]=sum[j],那麼從i到j的0和1數量必然相等。
所以,問題就轉化成了求sum數組中兩個相等元素的最長距離。

由於比賽時數組從0開始存,導致用map標記時對於第一個位置的元素出現了問題。

代碼

#include <bits/stdc++.h>
using namespace std;

typedef long long ll;
const int maxn = 200005;
template <class T>
void read(T &x) {
    T f=1;x=0;char s=getchar();
    while(s<'0'||s>'9') {if(s=='-')f=-1;s=getchar();}
    while(s>='0'&&s<='9') {x=x*10+s-'0';s=getchar();}
    x *= f;
}

int n,a[maxn],cnt1,cnt2,ans,res,b[maxn],sum[maxn];
map<int,int> mp;
string s;
int main() {
    read(n);
    cin >> s;
    if(s[0]=='0') a[0]=1,b[0]=0;
    else a[0]=0,b[0]=1;
    for(int i=0;i<n;i++) {
        if(s[i]=='0') cnt1++;
        else cnt2++;
        if(s[i]=='0' && i>0) a[i] = a[i-1]+1,b[i]=b[i-1];
        else if(i>0) a[i] = a[i-1], b[i] = b[i-1]+1;
        sum[i] = a[i] - b[i];
        if(sum[i]==0) ans = max(ans,i+1);
    }
    for(int i=0;i<n;i++) mp[sum[i]]=-1;
    for(int i=0;i<n;i++) {
        if(mp[sum[i]]==-1) mp[sum[i]] = i;
        else  ans = max(ans,i-mp[sum[i]]);
    }
    res = min(cnt1,cnt2)*2;
    cout << ans << ' ' << res << endl;
    return 0;
}

F. Planting Trees

題目

在n×n的矩陣裏,求出一個最大的矩形,使得該矩形中元素的 最大值-最小值≤m 。
其中 n500n\leq 500

題解

題目中已經說明了要用n3n^3的做法。
首先枚舉上下邊界u和d,維護ma[i]和mi[i]。
其中ma[i]表示從a[u][i]到a[d][i]的最大值,mi[i]就是這個區間的最小值。
已經得到同一列的最大最小值以後,可以再去維護左右邊界。
用r去表示右邊界從1往n掃,再用兩個單調隊列維護當前左右邊界中的ma[i]的最大值和mi[i]的最小值(因爲只看這個矩形內的最大最小值)。
如果m\leq m,那麼更新ans;
否則將左區間向右移動,並且更新兩個單調隊列。

代碼

#include <bits/stdc++.h>
using namespace std;

typedef long long ll;
const int maxn = 505;
template <class T>
void read(T &x) {
    T f=1;x=0;char s=getchar();
    while(s<'0'||s>'9') {if(s=='-')f=-1;s=getchar();}
    while(s>='0'&&s<='9') {x=x*10+s-'0';s=getchar();}
    x *= f;
}

int t,n,m,a[maxn][maxn],mi[maxn],ma[maxn];

int main() {
    read(t);
    while(t--) {
        read(n), read(m);
        int ans = 0;
        for(int i=1;i<=n;i++) 
            for(int j=1;j<=n;j++)
                read(a[i][j]);
        for(int d=1;d<=n;d++) {
            for(int i=1;i<=n;i++) ma[i]=mi[i]=a[d][i];
            for(int u=d;u<=n;u++) {
                int len = u-d+1; 
                for(int i=1;i<=n;i++) {
                    ma[i] = max(ma[i],a[u][i]);
                    mi[i] = min(mi[i],a[u][i]);
                }
                deque<int> p,q;
                p.clear(), q.clear();
                int l = 1;
                for(int i=1;i<=n;i++) {
                    while(!p.empty() && ma[p.back()]<=ma[i]) p.pop_back(); //維護最大
                    p.push_back(i);
                    while(!q.empty() && mi[q.back()]>=mi[i]) q.pop_back(); //維護最小
                    q.push_back(i);
                    if(!p.empty()&&!q.empty()) {
                        int maxi = p.front();
                        int mini = q.front();
                        if(ma[maxi]-mi[mini]<=m) {
                            ans = max(ans,(i-l+1)*len);
                        } else {
                            while(!p.empty() && !q.empty() && ma[p.front()]-mi[q.front()]>m) {
                                ++l;
                                if(p.front()<l) p.pop_front();
                                if(q.front()<l) q.pop_front(); 
                            }
                        }
                    }
                }
            }
        }
        cout << ans << endl;
    }
    return 0;
}

H. Magic Line

題目

在平面上有n個點,畫一條線將平面分成兩部分,使得兩部分的點個數一樣多。
其中n10001000xi,yi1000n\leq 1000, -1000\leq |x_i|,|y_i|\leq1000

題解

毒瘤題卡精度。
將點按橫縱座標排個序,然後對於中間兩個點,如果可以豎直着劃線就豎直着畫,否則就稍微傾斜一點。

代碼

#include <bits/stdc++.h>
using namespace std;
 
typedef long long ll;
const int maxn = 200005;
template <class T>
void read(T &x) {
    T f=1;x=0;char s=getchar();
    while(s<'0'||s>'9') {if(s=='-')f=-1;s=getchar();}
    while(s>='0'&&s<='9') {x=x*10+s-'0';s=getchar();}
    x *= f;
}
 
int t,n,a1,a2,b1,b2;
struct node{
    int x,y;
    bool operator<(const node &b)const {
        if(x==b.x) return y<b.y;
        return x<b.x;
    }
}e[maxn];
 
int main() {
    read(t);
    while(t--) {
        read(n);
        for(int i=0;i<n;i++) read(e[i].x), read(e[i].y);
        sort(e,e+n);
        int k = n/2 - 1;
        if(e[k+1].x-e[k].x>1) {
            a1 = a2 = e[k].x+1;
            b1 = 0;
            b2 = 1;
        } else {
            a1 = e[k].x-1;
            b1 = e[k].y+999000000+1;
            a2 = e[k].x+1;
            b2 = e[k].y-999000001+1;
        }
        cout << a1 << ' ' << b1 << ' ' << a2 << ' ' << b2 << endl;
    }
    return 0;
}

J. LRU management

本題要求實現一個可以按照序號查找的雙向循環鏈表,即可以完成增刪查改。本題內容篇幅較大,會在另一篇文章中詳細介紹。
傳送門:https://blog.csdn.net/zxwsbg/article/details/97496860

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章