天天看點

HRBU 2021暑期訓練解題報告階段三Day1目錄A - Similar StringsB - card card cardC - StringD - Complete the SequenceE - u Calculate eF - Maximum Subrectangle

目錄

A - Similar Strings

B - card card card

C - String

D - Complete the Sequence

E - u Calculate e

F - Maximum Subrectangle

G - Producing Snow

A - Similar Strings

題意:

給定字元串A與字元串B,如果有A的子串A'與B的子串B'滿足條件:

  1. length(A')==length(B')
  2. A'與B‘在相同位置上最多有k個字元不同

我們就認為A'與B'棒極了。

現在我想知道A'與B'最長是多長?

思路:

對兩個字元串分别進行尺取。

  • 不斷枚舉字元串B的子串B'的開始位置對字元串A進行尺取
  • 不斷枚舉字元串A的子串A'的開始位置對字元串B進行尺取
  • 考察點:字元串,尺取,枚舉,暴力

代碼:

#include<bits/stdc++.h>
using namespace std;
string s1,s2;
int ans,k,t;
int len1,len2;

int main(){

    cin>>t;
    while(t--){
        cin>>k;
        cin>>s1;
        cin>>s2;
        ans=-1;
        len1=s1.size(),len2=s2.size();
        for(int i=0;i<len1;i++){
            int l=0,r=0,cnt=0;
            while(i+r<len1&&r<len2){
                if(s1[i+r]!=s2[r])
                    cnt++;
                while(cnt>k){
                    if(s1[i+l]!=s2[l])
                        cnt--;
                    l++;
                }
                ans=max(ans,r-l+1);
                r++;
            }
        }
        for(int i=0;i<len2;i++){
            int l=0,r=0,cnt=0;
            while(r<len1&&i+r<len2){
                if(s1[r]!=s2[i+r])
                    cnt++;
                while(cnt>k){
                    if(s1[l]!=s2[i+l])
                        cnt--;
                    l++;
                }
                ans=max(ans,r-l+1);
                r++;
            }
        }
        cout<<ans<<endl;
    }
}
           

B - card card card

題意:

有一個長度為 n 的撲克牌序列a,當你拿第i張牌的時候有一個懲罰值bi,我們開始從第一張牌開始取牌,隻要拿的撲克牌點數之和不小于對應的懲罰值之和,則所拿撲克合法。

求在保證可以拿到的撲克牌點數之和最大的情況下,初始最少要拿幾張牌放到序列後面去?

思路:

首先既然需要将部分字元串放置在尾部,幹脆存兩倍長度(複制一份放在後面)。對序列進行尺取,分别計算(ai - bi)與ai的和,前者用來判斷取值結束,後者存儲最大值。

  • 考察點:尺取,序列和

代碼:

#include<bits/stdc++.h>
using namespace std;
const int maxn=2e6+100;
int a[maxn],b[maxn];  ///a[i]:第i張牌的價值  b[i]:第i張牌的懲罰值
int c[maxn];          ///c[i]:第i張牌的純收益  c[i]=a[i]-b[i]
int main()
{
    int n;
    ios::sync_with_stdio(false);
    while(cin>>n)
    {
        for(int i=0;i<n;i++)
            cin>>a[i];
        for(int i=0;i<n;i++)
            cin>>b[i];
        for(int i=0;i<n;i++)
            c[i]=a[i]-b[i];
        for(int i=n;i<2*n;i++)
            c[i]=a[i%n]-b[i%n];
        int l=0,r=0;
        int sum=0,ans=0,sumc=0,cnt=0;  ///ans:最大價值  sumc:目前收益 sum:目前的價值
        while(l<n){
            while(r<2*n&&sumc+c[r]>=0)
            {
                sumc+=c[r];
                r++;
                sum+=a[r];
            }
            if(sum>ans){
                ans=sum;
                cnt=l;
            }
            sumc-=c[l];
            sum-=c[l];
            l++;
        }
        cout<<cnt<<endl;
    }
}
           

C - String

題意:

給定一個字元串S,求出S有多少個子串滿足:

子串S'中不同字母的數量至少有k個。

思路:

對字元串S進行尺取。

目前尺取出的子串S'若滿足條件,則以它為開始字元串的子串都滿足條件。

  • 考察點:尺取,思維,字元串

代碼:

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn=1e6+100;
char ss[maxn];
int num[30];

int num_len()
{
    int len=0;
    for(int i=0;i<26;i++)
        if(num[i]) len++;
    return len;
}
int main(){
    int t;
    scanf("%d",&t);
    while(t--){
        int k;
        scanf("%s",ss);
        scanf("%d",&k);
        memset(num,false,sizeof(num));
        ll ans=0;
        int l=0,r=0,len=strlen(ss);
        while(true){
            while(num_len()<k&&r<len){
                num[ss[r++]-'a']++;
            }
            if(num_len()<k)
                break;
            ans+=len-r+1;
            num[ss[l++]-'a']--;
        }
        printf("%lld\n",ans);
    }
}
           

D - Complete the Sequence

題意:

根據給定的長度為S的序列的規律算出接下來C個元素的值。

思路:

如果數學好的話不難看出這題用到了拉格朗日插值法。

簡而言之就是多元差分數組。

解釋樣例:

原始資料:1 1 1 1 1 1 1 1 1 2 11 56
一階差分:0 0 0 0 0 0 0 0 1 9 45
二階差分:0 0 0 0 0 0 0 1 8 36
三階差分:0 0 0 0 0 0 1 7 28
四階差分:0 0 0 0 0 1 6 21
五階差分:0 0 0 0 1 5 15
六階差分:0 0 0 1 4 10
七階差分:0 0 1 3 6 
八階差分:0 1 2 3
九階差分:1 1 1
      

部落客比較笨,懂,但不知道怎麼描述。

HRBU 2021暑期訓練解題報告階段三Day1目錄A - Similar StringsB - card card cardC - StringD - Complete the SequenceE - u Calculate eF - Maximum Subrectangle
  • 考察點:拉格朗日插值,差分數組

代碼:

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

int num[500][500];
int main()
{
    int t,s,c;
    cin>>t;
    while(t--){
        cin>>s>>c;
        for(int i=0;i<s;i++)
            cin>>num[0][i];
        for(int i=1;i<s;i++){
            for(int j=0;j<s-i;j++)
                num[i][j]=num[i-1][j+1]-num[i-1][j];
        }
        for(int i=1;i<s+c;i++)
            num[s-1][i]=num[s-1][0];  //複制最後一層
        for(int i=s-2;i>=0;i--){
            for(int j=s-i;j<s+c;j++)
                num[i][j]=num[i+1][j-1]+num[i][j-1];  //倒推上一層
        }

        for(int i=0;i<c;i++){
            if(i) cout<<" ";
            cout<<num[0][s+i];
        }
        cout<<endl;
    }
    return 0;
}
           

E - u Calculate e

題意:

對于題目中給定的公式,求出n取值從0到9時e對應的值,并按照規定格式輸出。

思路:

沒有思路,暴力打表,阿巴阿巴

HRBU 2021暑期訓練解題報告階段三Day1目錄A - Similar StringsB - card card cardC - StringD - Complete the SequenceE - u Calculate eF - Maximum Subrectangle
  • 考察點:暴力,枚舉

代碼:

#include<bits/stdc++.h>
using namespace std;
int main()
{
    printf("n e\n");
    printf("- -----------\n");
    printf("0 1\n");
    printf("1 2\n");
    printf("2 2.5\n");
    printf("3 2.666666667\n");
    printf("4 2.708333333\n");
    printf("5 2.716666667\n");
    printf("6 2.718055556\n");
    printf("7 2.718253968\n");
    printf("8 2.718278770\n");
    printf("9 2.718281526\n");
    return 0;
}
           

F - Maximum Subrectangle

題意:

給定長度為n的序列a與長度為m的序列b,你可以得到一個n*m的矩陣c,滿足:

C( i , j )==Ai * Bj

現在找出C矩陣的最大子矩陣使得:

  1. 矩陣元素盡可能的多
  2. 矩陣和小于等于x

輸出這個矩陣的元素個數。

思路:

首先分别對序列a,序列b求字首和。

然後貪心出當序列a的子串長度為i時,序列a的子序列和的最小值,存儲起來。同時貪心出當序列b的子串長度為i時,序列b的子序列和的最小值,存儲起來。

接下來就通過兩個最小子序列和數組相乘得答案。

  • 考察點:字首和,貪心

代碼:

#include<bits/stdc++.h>
#define inf 0x3f3f3f3f3f
using namespace std;
typedef long long ll;
ll a[2005];
ll b[2005];
ll mina[2005];
ll minb[2005];
int main()
{
    int n,m;
    ll x;
    cin>>n>>m;
    a[0]=b[0]=0;
    for(int i=1; i<=n; i++)
    {
        cin>>a[i];
        a[i]+=a[i-1];
    }
    for(int i=1; i<=m; i++)
    {
        cin>>b[i];
        b[i]+=b[i-1];
    }
    cin>>x;
    memset(mina,inf,sizeof(mina));
    memset(minb,inf,sizeof(minb));
    for(int i=1;i<=n;i++)
        for(int j=i;j<=n;j++)
        mina[i]=min(mina[i],a[j]-a[j-i]);
    for(int i=1;i<=m;i++)
        for(int j=i;j<=m;j++)
        minb[i]=min(minb[i],b[j]-b[j-i]);
    int ans=0;
    for(int i=1;i<=n;i++)
        for(int j=1;j<=m;j++)
    {
        //cout<<i<<" "<<j<<" "<<mina[i]*minb[j]<<endl;
        if(mina[i]*minb[j]<=x){

            ans=max(ans,i*j);
        }
    }
    cout<<ans<<endl;
}
           

G - Producing Snow

題意:

每一天在你家門口會形成不同的雪堆。

每天早上形成高度為ai米的雪堆,晚上會因為溫度關系,所有雪堆最多減少bi米。

請給出每天晚上當天融化了多少雪?

思路:

對每天的雪的融化量求字首和。

對于每次的融化量考慮三種情況:

  1. 第i個雪堆高度大于等于融化量
  2. 第i個雪堆高度小于融化量
  3. 第i個雪堆高度為0
  • 考察點:字首和,思維

代碼:

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn=1e5+100;

ll a[maxn],b[maxn],c[maxn];
ll sur[maxn],num[maxn];
int main()
{
    int n;
    cin>>n;
    for(int i=1;i<=n;i++)
        cin>>a[i];
    c[0]=0;
    for(int i=1;i<=n;i++){
        cin>>b[i];
        c[i]=c[i-1]+b[i];
    }
    memset(num,0,sizeof(num));
    for(int i=1;i<=n;i++){
        int pos=upper_bound(c+1,c+1+n,a[i]+c[i-1])-c;
        sur[pos]+=a[i]+c[i-1]-c[pos-1];
        num[i]++;
        num[pos]--;
    }
    ll cnt=0;
    for(int i=1;i<=n;i++){
        cnt+=num[i];
        if(i!=1) cout<<" ";
        cout<<sur[i]+cnt*b[i];
    }
    cout<<endl;
}
           

繼續閱讀