初三奧賽模擬測試4

卡布叻_周深發表於2024-04-06

前言

image

\(CSP-S\) 模擬賽,確實比前幾次簡單多了。

  • \(T1~100pts\) : 簽到題。

  • \(T2~100pts\) :二分直接跑即可。

  • \(T3~40pts\)

    首先他給的這個快讀沒法用。

    賽時 joker 了,打了個 \(n^2~DP\) ,然後最佳化了 \(50min\) 換了個轉移方程還是 \(n^2\) ,複雜度打假了。

    因為賽時懶得跑線段樹,而且知道這玩意常數太大過不去,賽後聽有人說 \(ST\) 表才恍然大悟,成 joker 了 。

  • \(T4~0pts\) :光顧著調 \(T3\) 了,沒怎麼看。

  • 比賽連結

\(T1\) 最後一課

簽到題,沒什麼好說的,將軍飲馬問題

點選檢視程式碼
#include<bits/stdc++.h>
#define int __int128
#define endl '\n'
#define sort stable_sort
using namespace std;
const int N=2e5+10;
template<typename Tp> inline void read(Tp&x)
{
    x=0;register bool z=true;
    register char c=getchar();
    for(;c<'0'||c>'9';c=getchar()) if(c=='-') z=0;
    for(;'0'<=c&&c<='9';c=getchar()) x=(x<<1)+(x<<3)+(c^48);
    x=(z?x:~x+1);
}
void wt(int x){if(x>9)wt(x/10);putchar((x%10)+'0');}
void write(int x){if(x<0)putchar('-'),x=~x+1;wt(x);}
int k,x,y,xx,yy;
signed main()
{
	#ifndef ONLINE_JUDGE
    freopen("in.txt","r",stdin);
    freopen("out.txt","w",stdout);
    #endif
    read(k),read(x),read(y),read(xx),read(yy);
    if((y<=k&&yy<=k)||(y>=k&&yy>=k))
    {
        y=k*2-y;
        write((xx-x)*(xx-x)+(yy-y)*(yy-y));
        return 0;
    }
    write((xx-x)*(xx-x)+(yy-y)*(yy-y));
}

\(T2\) 日常

二分直接跑即可,記得排序

點選檢視程式碼
#include<bits/stdc++.h>
#define int long long 
#define endl '\n'
#define sort stable_sort
using namespace std;
const int N=1e5+10;
template<typename Tp> inline void read(Tp&x)
{
    x=0;register bool z=true;
    register char c=getchar();
    for(;c<'0'||c>'9';c=getchar()) if(c=='-') z=0;
    for(;'0'<=c&&c<='9';c=getchar()) x=(x<<1)+(x<<3)+(c^48);
    x=(z?x:~x+1);
}
void wt(int x){if(x>9)wt(x/10);putchar((x%10)+'0');}
void write(int x){if(x<0)putchar('-'),x=~x+1;wt(x);}
int n,m,k;
bool v[N];
struct aa
{
    int l,r,x,y;
}e[N];
bool cmp(aa a,aa b) {return a.l==b.l?a.r<b.r:a.l<b.l;}
int ask1(int i)
{
    int l=1,r=n,mid,ans=0;
    while(l<=r)
    {
        mid=(l+r)>>1;
        if(e[mid].l>i) r=mid-1;
        if(e[mid].r<i) l=mid+1;
        if(e[mid].l<=i&&e[mid].r>=i) 
        {
            ans=mid;
            break;
        }
    }
    return ans;
}
int ask2(int i)
{
    int l=1,r=n,mid,ans=0;
    while(l<=r)
    {
        mid=(l+r)>>1;
        if(e[mid].x>i) r=mid-1;
        if(e[mid].y<i) l=mid+1;
        if(e[mid].x<=i&&e[mid].y>=i) 
        {
            ans=mid;
            break;
        }
    }
    return ans;
}
signed main()
{
	#ifndef ONLINE_JUDGE
    freopen("in.txt","r",stdin);
    freopen("out.txt","w",stdout);
    #endif
    read(n),read(m),read(k);
    for(int i=1,l,r,x,y;i<=n;i++)
    {
        read(l),read(x),read(y),read(r);
        e[i]={l,r,x,y};
    }
    sort(e+1,e+1+n,cmp);
    for(int i;k>=1;k--)
    {
        read(i);
        int ans=ask1(i),anss=ask2(i);
        if(ans==0) puts("Failed");
        else if(v[ans]) puts("Again");
        else if(anss) puts("Perfect");
        else puts("Normal");
        v[ans]=1;
    }
}

\(T3\) 渡塵

部分分

  • \(20pts\)

    純暴力跑,\(O(n^2m)\)

  • \(40pts\)

    \(O(n^2)\) 區間 \(DP\)

    轉移方程:

    \[f_{l,r}=\max\{f_{l,r},abs(sum_r-sum_{l-1}),f_{l+1,r},f_{l,r-1}\} \]

    點選檢視程式碼
    #include<bits/stdc++.h>
    #define int long long 
    #define endl '\n'
    #define sort stable_sort
    using namespace std;
    const int N=5010;
    template<typename Tp> inline void read(Tp&x)
    {
        x=0;register bool z=true;
        register char c=getchar();
        for(;c<'0'||c>'9';c=getchar()) if(c=='-') z=0;
        for(;'0'<=c&&c<='9';c=getchar()) x=(x<<1)+(x<<3)+(c^48);
        x=(z?x:~x+1);
    }
    void wt(int x){if(x>9)wt(x/10);putchar((x%10)+'0');}
    void write(int x){if(x<0)putchar('-'),x=~x+1;wt(x);}
    int n,m,a[N],sum[N],f[N][N];
    signed main()
    {
        #ifndef ONLINE_JUDGE
        freopen("in.txt","r",stdin);
        freopen("out.txt","w",stdout);
        #endif
        read(n),read(m);
        for(int i=1;i<=n;i++)
            for(int j=1;j<=n;j++)
                f[i][j]=-0x3f3f3f3f;
        for(int i=1;i<=n;i++)
            read(a[i]),
            sum[i]=sum[i-1]+a[i],
            f[i][i]=abs(a[i]);
        for(int i=1;i<=n;i++)
            for(int j=i-1;j>=1;j--)
                f[i][j]=max({
                    f[i][j],
                    abs(sum[i]-sum[j-1]),
                    abs(sum[i]-sum[j]),
                    abs(sum[i-1]-sum[j-1]),
                    f[i][j+1],f[i-1][j],f[i-1][j+1]
                    });
        for(int l,r;m>=1;m--)
            read(l),read(r),
            write(f[r][l]),puts("");
    }
    
  • \(70pts\)

    線段樹維護區間最大子段和,類似 山海經 這道題。

    記錄每個區間的最大字首和,最大字尾和,最大欄位和,區間和。

    • 最大欄位和 \(=\) 左區間最大字尾 \(+\) 右區間最大字首。

    • 最大字首和 \(=\max\{\) 左區間最大字首和,左區間和 \(+\) 右區間最大字首和 \(\}\) 。最大字尾和同理。

    複雜度 \(O(m\log(n))\) ,理論可過,但由於線段樹的超級常數,只能拿 \(70pts\)

    程式碼沒打,沾 \(Hangry\) 的了。

    點選檢視程式碼
    #include <bits/stdc++.h>
    #define int long long 
    #define lson (id << 1) 
    #define rson (id << 1 | 1)
    using namespace std ; 
    const int N = 2e5 + 100 ; 
    inline int read() {
        int x = 0 , f = 1 ; 
        char c = getchar() ; 
    
        while ( c < '0' || c > '9' ) {
            if ( c == '-' ) f = -f ;  
            c = getchar() ; 
        }
    
        while ( c >= '0' && c <= '9' ) {
            x = x * 10 + c - '0' ; 
            c = getchar() ; 
        }
    
        return x * f ; 
    }
    int n , m ; 
    int a[N] ; 
    class AVST {
        public: 
            int Max , sum ; 
            int left_min , right_min ; 
            int left_max , right_max ; 
    } t[N << 2] ; 
    inline unsigned int Absolute ( int x ) {
        return x >= 0 ? x : -x ; 
    }
    inline int max( int a , int b ) {
        return a > b ? a : b ; 
    }
    inline int min( int a , int b ) {
        return a < b ? a : b ; 
    }
    inline void push_up( AVST &Main , AVST Lson , AVST Rson ) {
        Main.sum = Lson.sum + Rson.sum ; 
        Main.left_max = max( Lson.left_max , Lson.sum + Rson.left_max ) ; 
        Main.left_min = min( Lson.left_min , Lson.sum + Rson.left_min ) ; 
        Main.right_max = max( Rson.right_max , Rson.sum + Lson.right_max ) ; 
        Main.right_min = min( Rson.right_min , Rson.sum + Lson.right_min ) ; 
        Main.Max = max( max( Main.sum , max( Lson.Max , Rson.Max ) ) , max( Absolute( Lson.right_max + Rson.left_max ) , Absolute( Lson.right_min + Rson.left_min ) ) ) ; 
    }
    void build( int id , int l , int r ) {
        if ( l == r ) {
            t[id].Max = Absolute( a[l] ) ; 
            t[id].sum = t[id].left_max = t[id].left_min = t[id].right_max = t[id].right_min = a[l] ; 
            return ; 
        }
        int mid = ( l + r ) >> 1 ; 
        build( lson , l , mid ) ; build( rson , mid + 1 , r ) ; 
        push_up( t[id] , t[lson] , t[rson] ) ; 
    }
    AVST Query( int id , int l , int r , int x , int y ) {
        if ( x <= l && r <= y ) {
            return t[id] ; 
        }
        int mid = ( l + r ) >> 1 ; 
        if ( x <= mid && mid < y ) {
            AVST reso = {0 , 0 , 0 , 0 , 0 , 0} , lef , rig ; 
            lef = Query( lson , l , mid , x , y ) ; 
            rig = Query( rson , mid + 1 , r , x , y ) ; 
            push_up( reso , lef , rig ) ; 
            return reso ; 
        } else {
            if ( x <= mid ) return Query( lson , l , mid , x , y ) ; 
            if ( y >= mid + 1 ) return Query( rson , mid + 1 , r , x , y ) ; 
            AVST bre ; return bre ; 
        }
    }
    signed main() {
    #ifndef ONLINE_JUDGE
        freopen( "1.in" , "r" , stdin ) ; 
        freopen( "1.out", "w" ,stdout ) ; 
    #endif
        n = read() ; m = read() ; 
        for ( int i = 1 ; i <= n ; ++ i ) {
            a[i] = read() ; 
        }
        build( 1 , 1 , n ) ; 
        int x , y ; 
        for ( int i = 1 ; i <= m ; ++ i ) {
            x = read() , y = read() ; 
            if ( i == m ) printf( "%lld" , Query( 1 , 1 , n , x , y ).Max ) ;
            else printf( "%lld\n" , Query( 1 , 1 , n , x , y ).Max ) ;  
        }
    }
    
  • \(100pts\)

    因為沒有修改,用貓樹維護,\(O(n\log(n)+m)\) ,但是我不會。

正解

\(sum_i\) 表示字首和。

則區間 \([l\sim r]\) 和的絕對值就是 \(\max(sum_r-sum_{l-1},sum_{l-1}-sum_r)\)

那麼問題就轉化為在區間 \([l-1\sim r]\) 中找一個最大的 \(sum_x\) 和一個最小的 \(sum_y\)\(sum_x-sum_y\) 即為所求。

直接用 \(ST\) 表維護即可,複雜度 \(O(n\log(n)+m)\)

注意預處理時迴圈從 \(0\) 開始,因為 \(l-1\) 可能 \(=0\)

這個正解甚至比那些歪解簡單得多。

點選檢視程式碼
#include<bits/stdc++.h>
#define int long long 
#define endl '\n'
#define sort stable_sort
using namespace std;
const int N=2e5+10;
template<typename Tp> inline void read(Tp&x)
{
    x=0;register bool z=true;
    register char c=getchar();
    for(;c<'0'||c>'9';c=getchar()) if(c=='-') z=0;
    for(;'0'<=c&&c<='9';c=getchar()) x=(x<<1)+(x<<3)+(c^48);
    x=(z?x:~x+1);
}
void wt(int x){if(x>9)wt(x/10);putchar((x%10)+'0');}
void write(int x){if(x<0)putchar('-'),x=~x+1;wt(x);}
int n,m,a[N],sum[N],mx[N][30],mi[N][30];
void RMQ()
{
    for(int i=0;i<=n;i++)
        for(int j=0;j<=log2(n);j++)
            mi[i][0]=mx[i][0]=-0x3f3f3f3f;
    for(int i=0;i<=n;i++)
        mx[i][0]=sum[i],
        mi[i][0]=-sum[i];
    for(int j=1;j<=log2(n);j++)
        for(int i=0;i<=n-(1<<j)+1;i++)
            mx[i][j]=max(mx[i][j-1],mx[i+(1<<(j-1))][j-1]),
            mi[i][j]=max(mi[i][j-1],mi[i+(1<<(j-1))][j-1]);
}
int ask_mx(int l,int r)
{
    int t=log2(r-l+1);
    return max(mx[l][t],mx[r-(1<<t)+1][t]);
}
int ask_mi(int l,int r)
{
    int t=log2(r-l+1);
    return max(mi[l][t],mi[r-(1<<t)+1][t]);
}
signed main()
{
	#ifndef ONLINE_JUDGE
    freopen("in.txt","r",stdin);
    freopen("out.txt","w",stdout);
    #endif
    read(n),read(m);
    for(int i=1;i<=n;i++)
        read(a[i]),
        sum[i]=sum[i-1]+a[i];
    RMQ();
    for(int l,r;m>=1;m--)
        read(l),read(r),
        write(ask_mx(l-1,r)+ask_mi(l-1,r)),puts("");
}