2024初三集训模拟测试2

2024初三集训模拟测试2

A. 小P的2048 (100pts)

题目链接 HZOI

可能是OJ懒得好好出题了,题目都是图片。

一道大模拟,花些时间慢慢写就能A,我赛时好像调了 \(1h\) 吧。就是注意审题,易错的地方很多

  1. 合并是不能连续的,两个数字合并之后得到新数字不会在本次操作中继续合并。

  2. 有效操作是指操作后,(添加新数字之前)棋盘没有任何变化,数字位置变化也视为有效

  3. 游戏改动后,一旦遇到无效操作,就会结束游戏

其他没什么了。

Code

#include<bits/stdc++.h>
using namespace std;
#define int long long
int n,m,a[10][10],sx1,sx2,sy1,sy2,sv1,sv2;
int h[10][10];
int d,v,k,times,ans,r;
deque<int>q;
signed main()
{
    scanf("%lld%lld",&n,&m);
    scanf("%lld%lld%lld%lld%lld%lld",&sx1,&sy1,&sv1,&sx2,&sy2,&sv2);
    a[sx1][sy1]=sv1;
    a[sx2][sy2]=sv2;
    while(m--){
        r=0;
        scanf("%lld%lld%lld",&d,&k,&v);
        for(int i=1;i<=n;i++){
            for(int j=1;j<=n;j++){
                h[i][j]=a[i][j];
            }
        }
        if(d==0){
            for(int j=1;j<=n;j++){
                bool flag=0;
                for(int i=1;i<=n;i++){
                    if(a[i][j]!=0){
                        if(!q.empty()&&a[i][j]==q.back()&&flag==0){
                            q.pop_back();
                            q.push_back(a[i][j]*2);
                            ans+=a[i][j]*2;
                            flag=1;
                            a[i][j]=0;
                            continue;
                        }
                        q.push_back(a[i][j]);
                        a[i][j]=0;
                        flag=0;
                    }
                }
                for(int i=1;i<=n;i++){
                    if(q.empty())break;
                    a[i][j]=q.front();
                    q.pop_front();
                }
            }
        }
        else if(d==1){
            for(int j=1;j<=n;j++){
                bool flag=0;
                for(int i=n;i>=1;i--){
                    if(a[i][j]!=0){
                        if(!q.empty()&&a[i][j]==q.back()&&flag==0){
                            q.pop_back();
                            q.push_back(a[i][j]*2);
                            ans+=a[i][j]*2;
                            a[i][j]=0;
                            flag=1;
                            continue;
                        }
                        q.push_back(a[i][j]);
                        a[i][j]=0;
                        flag=0;
                    }
                }
                for(int i=n;i>=1;i--){
                    if(q.empty())break;
                    a[i][j]=q.front();
                    q.pop_front();
                }
            }
        }
        else if(d==2){
            for(int i=1;i<=n;i++){
                bool flag=0;
                for(int j=1;j<=n;j++){
                    if(a[i][j]!=0){
                        if(!q.empty()&&a[i][j]==q.back()&&flag==0){
                            q.pop_back();
                            q.push_back(a[i][j]*2);
                            ans+=a[i][j]*2;
                            a[i][j]=0;
                            flag=1;
                            continue;
                        }
                        q.push_back(a[i][j]);
                        a[i][j]=0;
                        flag=0;
                    }
                }
                for(int j=1;j<=n;j++){
                    if(q.empty())break;
                    a[i][j]=q.front();
                    q.pop_front();
                }
            }
        }
        else{
            for(int i=1;i<=n;i++){
                bool flag=0;
                for(int j=n;j>=1;j--){
                    if(a[i][j]!=0){
                        if(!q.empty()&&a[i][j]==q.back()&&flag==0){
                            q.pop_back();
                            q.push_back(a[i][j]*2);
                            ans+=a[i][j]*2;
                            a[i][j]=0;
                            flag=1;
                            continue;
                        }
                        q.push_back(a[i][j]);
                        a[i][j]=0;
                        flag=0;
                    }
                }
                for(int j=n;j>=1;j--){
                    if(q.empty())break;
                    a[i][j]=q.front();
                    q.pop_front();
                }
            }
        }
        bool work=0;
        for(int i=1;i<=n;i++){
            for(int j=1;j<=n;j++){
                if(a[i][j]!=h[i][j])work=1;
            }
        }
        if(work==0)break;

        times++;
        for(int i=1;i<=n;i++){
            for(int j=1;j<=n;j++){
                if(a[i][j]==0){
                    r++;
                }
            }
        }
        if(r==0)break;
        r=(k%r)+1;
        int tot=0;
        for(int i=1;i<=n;i++){
            for(int j=1;j<=n;j++){
                if(a[i][j]==0){
                    tot++;
                    if(tot==r)a[i][j]=v;
                }
            }
        }
    }
    printf("%lld\n%lld",times,ans);
}

B. 子集 (0pts)Special Judge

题目链接

构造题,将 $ 1,2,\dots ,n $ 划分为 \(k\) 个子集,使每个子集元素个数相同,元素之和相同,保证 $ k\mid n $

思路

令 $ s=\frac{n}{k} $ 即每个子集元素个数

\(s\) 为偶数时,构造方法显然,我们只要保证每两个元素加和相同,则构造的数列也一定相同,按照一种蛇形的方法构造即可。

\(s\) 为奇数时,相对复杂,我们保证每个子集前两项之和为等差数列,不妨令公差为1,以 \(n=9,k=3\) 为例,得到如下构造:

 9 5 1
 7 6 2
 8 4 3

发现 \(9+5=14,7+6=13,8+4=12\)

\(k\)为奇数时,我们求得前两项之和的平均数为\(p\),显然最大一项为 \(p+ \left \lfloor \frac{k}{2} \right \rfloor\),由大到小匹配,\(9对应的数字很明显是(14-9=5)\),若我们把 \(8\)\(13\) 对应起来,发现与上一组冲突了,所以可以匹配 \(7\)\(13\),最后匹配 \(8\)\(12\)

\(k\)为偶数时,\(s\)是奇数,\(n=s\times k\) 是偶数,

\[\sum_{i=1}^{n}=\frac{n\times (n+1)}{2} \]

平均数 $$ \frac{n\times (n+1)}{2\times k} $$
无解

Code

#include<bits/stdc++.h>
using namespace std;
const int N=1e6+100;
long long T,n,k,s;
vector<int>v[N];
signed main()
{
    scanf("%lld",&T);
    while(T--){
        scanf("%lld%lld",&n,&k);
        if(n==1){
            puts("Yes");
            puts("1");
            continue;
        }
        if((long long)(n*n+n)%(long long)(2*k)!=0||n==k){
            puts("No");
            continue;
        }
        s=n/k;
        puts("Yes");
        if(s%2==0){
            for(int i=1,j=1,l=1;i<=n;i++,j+=l){
                v[j].push_back(i);
                if(j==k&&l==1){
                    j=k+1;
                    l=-1;
                }
                if(j==1&&l==-1){
                    j=0;
                    l=1;
                }
            }
            for(int i=1;i<=k;i++){
                while(!v[i].empty()){
                    printf("%d ",v[i].back());
                    v[i].pop_back();
                }
                puts("");
            }
        }
        else{
            int w=n-2*k;
            long long p=((n*(n+1)/2)-(w*(w+1)/2))/k;
            long long t=p+k/2,ord=1;
            for(int i=1,j=n;i<=k;i+=2,j-=2,t--){
                v[ord].push_back(j);
                v[ord].push_back(t-j);
                ord++;
            }
            for(int i=2,j=n-1;i<=k;i+=2,j-=2,t--){
                v[ord].push_back(j);
                v[ord].push_back(t-j);
                ord++;
            }
            for(int i=1,j=1,l=1;i<=n-2*k;i++,j+=l){
                v[j].push_back(i);
                if(j==k&&l==1){
                    j=k+1;
                    l=-1;
                }
                if(j==1&&l==-1){
                    j=0;
                    l=1;
                }
            }
            for(int i=1;i<=k;i++){
                while(!v[i].empty()){
                    printf("%d ",v[i].back());
                    v[i].pop_back();
                }
                puts("");
            }
        }
    }
}

C. 混凝土粉末 (84pts)

题目链接

赛时做法

在线做法,我们用一个结构体存储每个修改操作,每遇到一个查询操作,遍历在这之前所有修改,将改点的高度累加,输出结果,对于这个做法,似乎人人都要问一句是怎么得\(84pts\)

复杂度分析

令出现 \(p\) 个1操作,\(q-p\) 个2操作,考虑最坏情况,所有查询都在修改后,复杂度 $p\times(q-p) $,显然二次函数,当 $ p=\frac{1}{2}\times q $,取最大值 $\frac{1}{4}\times q^2 $

又因为时间限制 $ 6000ms $ ,\(q\le 10^5\) 都可以跑过。

Code



#include<bits/stdc++.h>
using namespace std;
#define ll long long
const int N=1e6+100;
int n,q,p,num;
ll x,y;
struct node{
    int l,r,id;
    ll h;
}name[N];
ll height;
int main()
{
    scanf("%d%d",&n,&q);
    for(int t=1;t<=q;t++){
        scanf("%d",&p);
        if(p==1){
            ++num;
            scanf("%d%d%lld",&name[num].l,&name[num].r,&name[num].h);
            name[num].id=t;
        }
        else{
            scanf("%lld%lld",&x,&y);
            height=0;
            bool flag=0;
            for(int i=1;i<=num;i++){
                if(name[i].l<=x&&x<=name[i].r){
                    height+=name[i].h;
                    if(height>=y){
                        flag=1;
                        printf("%d\n",name[i].id);
                        break;
                    }
                }
            }
            if(flag==0)puts("0");
        }
    }
}

正解

树状数组+二分

我们将每个修改差分,当前坐标的高度就是差分后的前缀和,用树状树组维护。对于查询操作,我们要保证两个限制:

  1. 只有修改的操作编号在查询前才会对结果有效

  2. 坐标小于当前节点的前缀和才会有效

对于普通的树状数组2很好维护,但不能保证1,因此有一定改变。

树状数组下标为操作编号,并枚举x坐标,显然能保证限制,并且我们发现随操作增加,当前坐标的高度单调不下降,考虑二分答案。

Code



#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define int long long
const int N=1e6+7;
int n,q,t[N],l,r;
ll x,y,h;
struct change{
    int id;
    ll num;
};
struct query{
    int id;
    ll yyy;
};
vector<change>g[N];
vector<query>p[N];
ll tree[N],answer[N];
inline int lowbit(int x){
    return x&(-x);
}
void Add(int x,int c){
    for(int i=x;i<=q;i+=lowbit(i)){
        tree[i]+=c;
    }
}
ll Query(int x){
    ll ans=0;
    for(int i=x;i>=1;i-=lowbit(i)){
        ans+=tree[i];
    }
    return ans;
}
signed main()
{
    #ifndef ONLINE_JUDGE
    freopen("1.in","r",stdin);
    freopen("1.out","w",stdout);
    #endif
    scanf("%d%d",&n,&q);
    for(int i=1;i<=q;i++){
        scanf("%d",&t[i]);
        if(t[i]==1){
            scanf("%d%d%lld",&l,&r,&h);
            g[l].push_back({i,h});
            g[r+1].push_back({i,-h});
        }
        else{
            scanf("%lld%lld",&x,&y);
            p[x].push_back({i,y});
        }
    }
    for(int i=1;i<=n;i++){
        for(auto j:g[i]){
            Add(j.id,j.num);
        }
        for(auto j:p[i]){
            if(Query(j.id)<j.yyy){
                answer[j.id]=0;
                continue;
            }
            int left=1,right=j.id-1;
            while(left<=right){
                int mid=(left+right)>>1;
                if(Query(mid)>=j.yyy)right=mid-1;
                else left=mid+1;
            }
            answer[j.id]=left;
        }
    }
    for(int i=1;i<=q;i++){
        if(t[i]==2){
            printf("%lld\n",answer[i]);
        }
    }
}

D. 排水系统(0pts)

不会,咕了

闲话

四题一共 \(400pts\) ,但是Shadow在赛后取得了 \(500pts\) 的好成绩,因为T1本来出的是原题“谜之阶乘”,Shadow通过贺代码40秒首A,后来题目更改了,Shadow得分没有重置,累加到原分数上。

现在知道谁找原题最快了。

posted @ 2024-02-22 07:11  Abnormal123  阅读(36)  评论(2)    收藏  举报