[61] (多校联训) A层冲刺NOIP2024模拟赛18

无论从什么意义上都能称得上挂 75 分的一场

A.选彩笔

好题

答案显然可以二分

突然发现我好像专长二分答案

钦定最大差值 \(dx\),将所有物品以 \((r,g,b)\) 看成三维空间坐标内的点,原问题可以转化成问空间里一个边长为 \(dx\) 的立方体内是否有至少 \(k\) 个点

考虑到值域不大,可以钦定立方体的一个顶点,以此来确定这个立方体,就变成了三维坐标下求给定坐标区间内点的个数问题

三维前缀和可以处理

然后说一下这个三维前缀和,之前模拟赛那个原题场考过一道 abc 的三维前缀和板子,和二维前缀和类似,简单容斥一下

\[s_{i,j,k}=s_{i-1,j,k}+s_{i,j-1,k}+s_{i,j,k-1}-s_{i-1,j-1,k}-s_{i-1,j,k-1}-s_{i,j-1,k-1}+s_{i-1,j-1,k-1}+a_{i,j,k} \]

要说咋记这玩意,其实最好的办法应该是找规律,你会发现 \(()-1\) 的个数的奇偶性是和符号相关的,然后根据首项(两项固定项)一定为正往下顺着推就行了

以防你像 CTH 一样求出前缀和不会用:

\((i',j',k')\)\((i,j,k)\) 立方体内的前缀和为:

\[s_{i,j,k}-s_{i'-1,j,k}-s_{i,j'-1,k}-s_{i,j,k'-1}+s_{i'-1,j'-1,k}+s_{i'-1,j,k'-1}+s_{i,j'-1,k'-1}-s_{i'-1,j'-1,k'-1} \]

推法和上述还是一样的

  • 为了避免前缀和式子出现负下标你可能需要特殊处理一下,我直接全都加一了
int n,K;
int maxr,maxg,maxb;
int r[100001],g[100001],b[100001];
int cnt[257][257][257];
int sum[257][257][257];
int cal(int x1,int x2,int y1,int y2,int z1,int z2){
    return sum[x2][y2][z2]-sum[x1-1][y2][z2]-sum[x2][y1-1][z2]-sum[x2][y2][z1-1]+sum[x1-1][y1-1][z2]+sum[x1-1][y2][z1-1]+sum[x2][y1-1][z1-1]-sum[x1-1][y1-1][z1-1];
}
bool check(int maxdx){
    for(int i=1;i<=maxr;++i){
        for(int j=1;j<=maxg;++j){
            for(int k=1;k<=maxb;++k){
                if(cal(i,min(maxr,i+maxdx),j,min(maxg,j+maxdx),k,min(maxb,k+maxdx))>=K){
                    return true;
                }
            }
        }
    }
    return false;
}
int main(){
    scanf(n,K);
    for(int i=1;i<=n;++i){
        scanf(r[i],g[i],b[i]);
        cnt[r[i]+1][g[i]+1][b[i]+1]++;
        maxr=max(maxr,r[i]+1);
        maxg=max(maxg,g[i]+1);
        maxb=max(maxb,b[i]+1);
    }
    for(int i=1;i<=maxr;++i){
        for(int j=1;j<=maxg;++j){
            for(int k=1;k<=maxb;++k){
                sum[i][j][k]=sum[i-1][j][k]+sum[i][j-1][k]+sum[i][j][k-1]-sum[i-1][j-1][k]-sum[i-1][j][k-1]-sum[i][j-1][k-1]+sum[i-1][j-1][k-1]+cnt[i][j][k];
            }
        }
    }
    int l=0,r=max({maxr,maxg,maxb}),ans=r;
    while(l<=r){
        int mid=(l+r)/2;
        if(check(mid)){
            r=mid-1;
            ans=mid;
        }
        else l=mid+1;
    }
    cout<<ans;
}

B.兵蚁排序

也是成功挂上分了

注意到可交换次数高达 \(n^2\) 次,考虑冒泡排序,每次交换邻项元素

因为我们交换的方式是基于值排序,因此

  • 将一个值与其前面的值交换,当且仅当该值小于(等于)前方的值
  • 将一个值与其后面的值交换,当且仅当该值大于(等于)前方的值

按冒泡排序的思路暴力将 \(a\) 数组交换到 \(b\) 数组,若存在无法到达的目标位置则报告无解

跑出来和大样例一模一样,看来 std 也是这么写的

UPD: 关于什么是冒泡排序的思路

从前到后枚举每个值,然后寻找应该被挪到哪个位置(注意到这个位置一定在它后面,因为它前面的值已经归位了),然后通过暴力交换换过去

int n;
int a[1001],b[1001];
vector<pair<int,int>>op;
bool move(int pos,int to){
    for(int i=pos;i>to;--i){
        if(a[i-1]>=a[i]){
            swap(a[i],a[i-1]);
            op.push_back({i-1,i});
        }
        else{
            return false;
        }
    }
    return true;
}
int main(){
    freopen("sort.in","r",stdin);
    freopen("sort.out","w",stdout);
    int cases;
    scanf(cases);
    while(cases--){
        op.clear();
        scanf(n);
        for(int i=1;i<=n;++i){
            scanf(a[i]);
        }
        for(int i=1;i<=n;++i){
            scanf(b[i]);
        }
        bool flag=false;
        for(int i=1;i<=n;++i){
            for(int j=i;j<=n;++j){
                if(a[j]==b[i]){
                    if(move(j,i));
                    else{
                        flag=true;
                        break;
                    }
                }
            }
            if(flag) break;
        }
        if(flag) cout<<-1<<'\n';
        else{
            cout<<0<<'\n';
            cout<<op.size()<<'\n';
            for(pair<int,int>i:op){
                cout<<i.first<<" "<<i.second<<'\n';
            }
        }
    }
}
赛时 checker

这一份没法判无解

/**
 * 
 * ------ checker for T2 ------*/

#include<bits/stdc++.h>
using namespace std;
int cases;
int n;
int a[100001],b[100001];
int main(){
    ifstream in("sort.in"),out("sort.out");
    in>>cases;for(int p=1;p<=cases;++p){
        in>>n;
        for(int i=1;i<=n;++i){
            in>>a[i];
        }
        for(int i=1;i<=n;++i){
            in>>b[i];
        }
        int opt;
        out>>opt;
        if(opt==-1){
            cout<<"Testcase "<<p<<" : no solution exist"<<endl;
        }
        else{
            int m,x,y;out>>m;
            if(m>n*n){
                cout<<"Testcase "<<p<<" : TOO MUCH OPERATIONS"<<endl;
                continue;
            }
            while(m--){
                out>>x>>y;
                swap(a[x],a[y]);
            }
            int flag=0;
            for(int i=1;i<=n;++i){
                if(a[i]!=b[i]){
                    flag=i;
                    break;
                }
            }
            if(flag){
                cout<<"Testcase "<<p<<" : FIND WRONG ANSWER ON PLACE "<<flag<<endl;
                for(int i=1;i<=n;++i){
                    cout<<a[i]<<" ";
                }
                cout<<endl;
                for(int i=1;i<=n;++i){
                    cout<<b[i]<<" ";
                }
                cout<<endl;
            }
            else cout<<"Testcase "<<p<<" : answer is correct"<<endl;
        }
    }
}

C.人口局 DBA

在做了


这是什么

posted @ 2024-11-05 19:09  HaneDaniko  阅读(28)  评论(2编辑  收藏  举报