初三奥赛模拟测试4

前言

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("");
}

\(T4\) 罪人挽歌

前言

题面中说若不满足字典序最小则获得 \(50\%\) 分数,但是喵喵没有搬到 \(Special~judge\) ,所以用 \(50\%\) 的数据只有一组解代替。

题面说采用捆绑测试,防止直接输出 No 骗分,没有搬到捆绑测试,所以数据中压根没有 No

部分分

  • \(20pts\) :暴力枚举所有排列。

  • \(50pts\)

    假设他有解,考虑解的形状,发现若满足 \(a_i=a_{i-1}\) ,那么一定满足 \(b_i=b_{i+1}\) ,反之亦然。

    考虑建图,\(a_i\)\(b_i\) 连双向边。

    欧拉回路 ,如果无法跑欧拉回路且懒得跑欧拉路径的话,不妨将两个奇点连起来,最后再删掉,就可以跑欧拉回路了。

    显然如果奇点个数 \(\neq 0\)\(\neq 2\) ,那就是无解了。

    至于怎么跑欧拉回路在正解中说,如果欧拉回路直接打假了就是 \(20pts\)

    那么如果欧拉回路复杂度没有假的话,因为不满足字典序最小,理论获得 \(50pts\)

  • \(70pts\)

    采用正解方法,但是欧拉回路复杂度假了。

    实际上就是删边要直接删,不能惰性删。

    点击查看代码
    #include<bits/stdc++.h>
    #define int long long 
    #define endl '\n'
    #define sort stable_sort
    using namespace std;
    const int N=2e6+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,s1,s2,d[N],a[N],b[N],cnt,ans1[N],ans2[N];
    bool v[N];
    stack<int>q;
    vector<int>e[N],f[N];
    struct aa
    {
        int a,b;
    }to[N];
    void dfs(int x,bool fa)
    {
        if(fa==0)
        {
            for(int i:e[x])
                if(!v[i])
                {
                    v[i]=1;
                    int y;
                    if(x==to[i].a) y=to[i].b;
                    else y=to[i].a;
                    dfs(y,1);
                    q.push(i);
                }
        }
        else 
        {
            for(int i:f[x])
                if(!v[i])
                {
                    v[i]=1;
                    int y;
                    if(x==to[i].a) y=to[i].b;
                    else y=to[i].a;
                    dfs(y,0);
                    q.push(i);
                }
        }
    }
    signed main()
    {
        #ifndef ONLINE_JUDGE
        freopen("in.txt","r",stdin);
        freopen("out.txt","w",stdout);
        #endif
        puts("Yes");
        read(n);
        for(int i=1;i<=n;i++)
            read(a[i]),read(b[i]),
            to[i]={a[i],b[i]},
            e[a[i]].push_back(i),
            f[b[i]].push_back(i),
            d[a[i]]++,d[b[i]]++;
        for(int i=1;i<=n;i++)
            if(d[i]==0) continue;
            else if(!s1&&(d[i]&1)) s1=i;
            else if(s1&&(d[i]&1))
            {
                s2=i;
                break;
            }
        to[n+1]={s1,s2},
        e[s1].push_back(n+1),
        e[s2].push_back(n+1);
        dfs(a[1],0);
        for(;!q.empty();q.pop())
            if(q.top()!=n+1)
                ans1[++cnt]=q.top();
        memset(v,0,sizeof(v)),cnt=0;
        dfs(b[1],1);
        for(;!q.empty();q.pop())
            if(q.top()!=n+1)
                ans2[++cnt]=q.top();
        for(int i=1;i<=n;i++)
            if(ans1[i]>ans2[i])
            {
                for(int j=1;j<=n;j++)
                    cout<<ans2[j]<<' ';
                return 0;
            }
            else if(ans1[i]<ans2[i])
            {
                for(int j=1;j<=n;j++)
                    cout<<ans1[j]<<' ';
                return 0;
            }
        for(int i=1;i<=n;i++)
            cout<<ans1[i]<<' ';
    }
    

正解

显然想要他字典序最小的话,选择从第一个边开始跑,那么就有两种情况,从 \(a_1\) 开始跑或从 \(b_1\) 开始跑。

如果后面的 \(dfs\) 也尽可能从靠前的边开始跑,那么显然就是字典序最小的了。

那么答案就是从 \(a_1\) 开始跑和从 \(b_1\) 开始跑两种情况字典序较小的一个。

接下来说这个欧拉回路怎么跑。

根据若 \(a_i=a_{i-1}\) 则一定 \(b_i=b_{i+1}\) ,我们应该蛇形跑

也就是说如果这一次是 \(a→b\) ,那么下一次就 \(b→a\) ,反之亦然,处理一下他这次应该怎么跑就可以了。

同时这个欧拉回路和正常的不一样,他存的是边,所以栈里存的就是边。

不妨建图的时候记录每一条边连着哪两个点,然后开两个 \(vector\) ,一个存 \(a→b\) ,一个存 \(b→a\) ,将这条边压到两个点里,这样的话跑的时候也是边最小的。

另外不能使用惰性删,要直接删,惰性删的复杂度无法接受。

那么怎么直接删呢?板子如下:

void dfs(int x)
{
    for(int i=id[x];i<e[x].size();i=max(i+1,id[x]))
    {
        if(e[x][i].second==0)
        {
            id[x]=i+1;
            e[x][i].second=1;            
            dfs(e[x][i].first);
        }
    }
    s.push(x);
}

当然开不开 \(pair\) 无所谓,开个 \(bool\) 也行,主要是这个 \(id\) 数组。

问题解决。

点击查看代码
#include<bits/stdc++.h>
#define int long long 
#define endl '\n'
#define sort stable_sort
using namespace std;
const int N=2e6+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,s1,s2,d[N],a[N],b[N],cnt,ans1[N],ans2[N],id[N],id2[N];
bool v[N];
stack<int>q;
vector<int>e[N],f[N];
struct aa
{
    int a,b;
}to[N];
void dfs(int x,bool fa)
{
    if(fa==0)
    {
        for(int i=id[x];i<e[x].size();i=max(id[x],i+1))
            if(!v[e[x][i]])
            {
                id[x]=i+1;
                v[e[x][i]]=1;
                int s=e[x][i];
                int y;
                if(x==to[s].a) y=to[s].b;
                else y=to[s].a;
                dfs(y,1);
                q.push(s);
            }
    }
    else 
    {
        for(int i=id2[x];i<f[x].size();i=max(id2[x],i+1))
            if(!v[f[x][i]])
            {
                id2[x]=i+1;
                v[f[x][i]]=1;
                int s=f[x][i];
                int y;
                if(x==to[s].a) y=to[s].b;
                else y=to[s].a;
                dfs(y,0);
                q.push(s);
            }
    }
}
signed main()
{
	#ifndef ONLINE_JUDGE
    freopen("in.txt","r",stdin);
    freopen("out.txt","w",stdout);
    #endif
    puts("Yes");
    read(n);
    for(int i=1;i<=n;i++)
        read(a[i]),read(b[i]),
        to[i]={a[i],b[i]},
        e[a[i]].push_back(i),
        f[b[i]].push_back(i),
        d[a[i]]++,d[b[i]]++;
    for(int i=1;i<=n;i++)
        if(d[i]==0) continue;
        else if(!s1&&(d[i]&1)) s1=i;
        else if(s1&&(d[i]&1))
        {
            s2=i;
            break;
        }
    to[n+1]={s1,s2},
    e[s1].push_back(n+1),
    e[s2].push_back(n+1);
    dfs(a[1],0);
    for(;!q.empty();q.pop())
        if(q.top()!=n+1)
            ans1[++cnt]=q.top();
    memset(v,0,sizeof(v));
    memset(id,0,sizeof(id));
    memset(id2,0,sizeof(id2));
    cnt=0;
    dfs(b[1],1);
    for(;!q.empty();q.pop())
        if(q.top()!=n+1)
            ans2[++cnt]=q.top();
    for(int i=1;i<=n;i++)
        if(ans1[i]>ans2[i])
        {
            for(int j=1;j<=n;j++)
                cout<<ans2[j]<<' ';
            return 0;
        }
        else if(ans1[i]<ans2[i])
        {
            for(int j=1;j<=n;j++)
                cout<<ans1[j]<<' ';
            return 0;
        }
    for(int i=1;i<=n;i++)
        cout<<ans1[i]<<' ';
}
posted @ 2024-04-06 15:02  卡布叻_周深  阅读(38)  评论(6编辑  收藏  举报