NOIP2025模拟赛27

T1 T2 T3 T4
\(\color{#FFC116} 普及/提高-\) \(\color{#9D3DCF} 省选/NOI-\) \(\color{#9D3DCF} 省选/NOI-\) \(\color{#9D3DCF} 省选/NOI-\)

参赛网址:https://oj.33dai.cn/d/TYOI/contest/68a74ad1c5d9c2f14c29ad14

剧透:有一道小模拟,远小于《梦幻金花》的小模拟。

T1 信道传输【2022NOIP模拟赛T1】

题目传送门

题目难度:\(\color{#FFC116} 普及/提高-\)

算法标签:搜索,枚举,二进制拆分

思路

每次枚举每一个 \(i = 2^k\),找到它的管理范围,判断是非符合标准,因为最多只错一个,所以如果它和它的管辖范围内的所有数一定是正确的。

所以我们枚举所有的 \(i = 2^k\) 这样我们就能找到不可判断的位置,如果这样我们就能找到不可判断的位置中 \(i = 2^k\) 的数量大于1,则不是 \(i = 2^k\) 的问题,而是所有的 \(i = 2^k\)\(i\) 的异或和(代码中不是这种写法,更复杂一些,也差不多了)。否则就是那个唯一的 \(i = 2^k\) 的问题。

AC Code

#include <bits/stdc++.h>
#define int long long
using namespace std;

const int maxn=16;
int T;
int n;
int vis[1<<(maxn+1)],num[1<<(maxn+1)];
string s;

signed main(){
    // freopen("code2.in","r",stdin);
    // freopen("code.out","w",stdout);
    ios::sync_with_stdio(false);
    cin.tie(0);
    cin>>T>>n;
    int len=(1<<n)-1;
    while (T--){
        for (int i=1;i<=len;i++)    vis[i]=0,num[i]=0;
        cin>>s;
        s='0'+s;
        for (int i=1;i<=len;i<<=1){
            int sum=0;
            for (int j=i+1;j<=len;j++){
                if ((j&i)==i)
                    sum^=(s[j]-'0');
            }
            if (sum!=(s[i]-'0')){
                vis[i]=max(vis[i],0ll);
                for (int j=i+1;j<=len;j++)
                    if ((j&i)==i)
                        vis[j]=max(vis[j],0ll),num[j]++;
            }
            else {
                vis[i]=1;
                for (int j=i+1;j<=len;j++)
                    if ((j&i)==i)
                        vis[j]=1;
            }
        }
        int cnt=0;
        for (int i=1;i<=len;i<<=1)
            if (vis[i]==0)  cnt++;
        if (cnt==0){
            for (int i=1;i<=len;i++){
                if (vis[i]==0){
                    if (s[i]=='0')    s[i]='1';
                    else    s[i]='0';
                }
            }
        }
        else if (cnt==1){
            for (int i=1;i<=len;i<<=1)
                if (vis[i]==0){
                    if (s[i]=='0')    s[i]='1';
                    else    s[i]='0';
                }
            
        }
        else {
            for (int i=1;i<=len;i++)
                if (vis[i]==0&&num[i]>=cnt){
                    if (s[i]=='0')    s[i]='1';
                    else    s[i]='0';
                }
        }
        for (int i=1;i<=len;i++)    cout<<s[i];
        cout<<"\n";
    }
    return 0;
}

T2 模拟交易【2022NOIP模拟赛T2】

题目传送门

题目难度:\(\color{#9D3DCF} 省选/NOI-\)

算法标签:模拟,优先队列

思路

这就是一道大模拟,结案。

《关于我给 @Sunpicnic 调了一下午代码的事情》

AC Code

#include <bits/stdc++.h>
#define int long long
using namespace std;

const int maxn=1e5+5;
int n,Q;
int cnt;
int las;
int a[maxn],c[maxn];//一开始第i个人手上有a[i]股贵州茅台的股票以及 c[i] 元现金
int vis[maxn];
int op,id,x,y;
struct gmdingdan{
    int a,x,y,ti;
    friend bool operator < (const gmdingdan &x,const gmdingdan &y){
        if (x.x==y.x)   return x.ti>y.ti;
        return x.x<y.x;
    }
};
struct smdingdan{
    int a,x,y,ti;
    friend bool operator < (const smdingdan &x,const smdingdan &y){
        if (x.x==y.x)   return x.ti>y.ti;
        return x.x>y.x;
    }
};
priority_queue<gmdingdan> gmc;
priority_queue<smdingdan> smc;

/*
1 a x y

表示第a个人想用x元每股的单价购买y股贵州茅台的股票。此时,如果他手上的钱足够x y,
那么,查找售卖池中是否有卖单,如果有,则将卖价小于等于x的卖单,按照卖价从小到大的顺序取出,
然后依次以卖价交易,直到这笔交易完整完成,或是没有满足条件的卖单。
此时,该笔交易剩余的需求进入购买池中。(购买池就是一堆买单组成的集合)

如果他手上的钱不够x×y,或者他已经有一笔买单或者卖单尚未交易完毕,则本次需求被驳回。

特别的,如果x=0,则代表购买价为当前交易系统最后一次的成交价格,如果没有成交过,则为0。
*/

void goumai(int id,int x,int y,int ti){//表示第a个人想用x元每股的单价购买y股贵州茅台的股票。
    if (c[id]>=x*y&&vis[id]==0){//此时,如果他手上的钱足够x×y
        vis[id]=ti;//他有一笔订单
        int tot=y;
        while (smc.size()>0&&tot>0){//如果有,则将卖价小于等于x的卖单,按照卖价从小到大的顺序取出,
            smdingdan t=smc.top();
            if (t.x>x)  break;//取出卖价小于等于x的卖单
            smc.pop();
            if (vis[t.a]!=t.ti)    continue;
            else {
                //然后依次以卖价交易,直到这笔交易完整完成,或是没有满足条件的卖单。
                if (tot>=t.y){
                    vis[t.a]=0;
                    //交易
                    a[id]+=t.y;c[id]-=t.x*t.y;
                    a[t.a]-=t.y;c[t.a]+=t.x*t.y;
                    //交易成功
                    cnt++;
                    las=t.x;
                    tot-=t.y;
                }
                else {//这笔交易完整完成
                    //交易
                    a[id]+=tot;c[id]-=t.x*tot;
                    a[t.a]-=tot;c[t.a]+=t.x*tot;
                    //交易成功
                    cnt++;
                    las=t.x;
                    smc.push({t.a,t.x,t.y-tot,t.ti});
                    tot=0;
                    break;
                }
            }
        }
        if (tot==0){//直到这笔交易完整完成
            vis[id]=0;
            return ;
        }
        else    gmc.push({id,x,tot,ti});//此时,该笔交易剩余的需求进入购买池中。(购买池就是一堆买单组成的集合)
    }
    else {
        //如果他手上的钱不够x×y,或者他已经有一笔买单或者卖单尚未交易完毕,则本次需求被驳回。
        return ;
    }
}

/*
2 a x y

表示第a个人想用x元每股的单价卖出y股。此时,如果他手上的股票数足够y股,
则,查找购买池中是否有买单,如果有,将买单价格大于等于x的买单,按照买价从大到小的顺序取出,
依次以买价交易,直到这笔交易完整完成,或是没有满足条件的买单。
此时,该笔交易剩余的需求进入售卖池中。

如果他手上的股票不够y,或者他已经有一笔买单或者卖单尚未交易完毕,则本次需求被驳回。

特别的,如果x=0,则代表售卖价为当前交易系统最后一次的成交价格,如果没有成交过,则为0。
*/

void shoumai(int id,int x,int y,int ti){//表示第a个人想用x元每股的单价卖出y股
    if (a[id]>=y&&vis[id]==0){//此时,如果他手上的股票数足够y股
        vis[id]=ti;//他有一笔订单
        int tot=y;
        while (gmc.size()>0&&tot>0){//如果有,将买单价格大于等于x的买单,按照买价从大到小的顺序取出,
            gmdingdan t=gmc.top();
            if (t.x<x)  break;//取出单价大于等于x的买单
            gmc.pop();
            if (vis[t.a]!=t.ti)    continue;
            else {
                //依次以买价交易,直到这笔交易完整完成,或是没有满足条件的买单
                if (tot>=t.y){
                    vis[t.a]=0;
                    //交易
                    a[id]-=t.y;c[id]+=t.x*t.y;
                    a[t.a]+=t.y;c[t.a]-=t.x*t.y;
                    //交易成功
                    cnt++;
                    las=t.x;
                    tot-=t.y;
                }
                else {//这笔交易完整完成
                    //交易
                    a[id]-=tot;c[id]+=t.x*tot;
                    a[t.a]+=tot;c[t.a]-=t.x*tot;
                    //交易成功
                    cnt++;
                    las=t.x;
                    gmc.push({t.a,t.x,t.y-tot,t.ti});
                    tot=0;
                    break;
                }
            }
        }
        if (tot==0){//直到这笔交易完整完成
            vis[id]=0;
            return ;
        }
        else    smc.push({id,x,tot,ti});
    }
    else {
        //如果他手上的股票不够y,或者他已经有一笔买单或者卖单尚未交易完毕,则本次需求被驳回
        return ;
    }
}

/*
3 a

表示第a个人想撤回自己正在交易的需求。
如果他有一笔买单或卖单尚未交易完成,则撤回剩余未交易成功的订单,否则,他的需求被驳回。

注意:在交易时,如果有多笔符合条件的订单,则按价格的优先级排序,如果价格一样,则按照订单被生成的时间从小到大排序。
*/

void chehui(int id){
    //如果他有一笔买单或卖单尚未交易完成,则撤回剩余未交易成功的订单
    vis[id]=0;
    //否则,他的需求被驳回
    return ;
}

signed main(){
    ios::sync_with_stdio(false);
    cin.tie(0);
    cin>>n>>Q;
    for (int i=1;i<=n;i++)  cin>>a[i];
    for (int i=1;i<=n;i++)  cin>>c[i];
    for (int i=1;i<=Q;i++){
        cin>>op>>id;
        if (op==1){//表示第a个人想用x元每股的单价购买y股贵州茅台的股票
            cin>>x>>y;
            if (x==0)   x=las;
            goumai(id,x,y,i);
        }
        else if (op==2){//表示第a个人想用x元每股的单价卖出y股
            cin>>x>>y;
            if (x==0)   x=las;
            shoumai(id,x,y,i);
        }
        else {//表示第a个人想撤回自己正在交易的需求
            chehui(id);
        }
    }
    cout<<cnt<<"\n";
    for (int i=1;i<=n;i++)  cout<<a[i]<<" ";cout<<"\n";
    for (int i=1;i<=n;i++)  cout<<c[i]<<" ";cout<<"\n";
    return 0;
}

T3 鲤鱼跃龙门【2022NOIP模拟赛T3】

题目传送门

题目难度:\(\color{#9D3DCF} 省选/NOI-\)

算法标签:区间DP,笛卡尔树,状态优化DP

T4 融合药剂【2022NOIP模拟赛T4】

题目传送门

题目难度:\(\color{#9D3DCF} 省选/NOI-\)

算法标签:重构树,线段树合并,虚树

posted @ 2025-10-01 12:43  Zzqyoung1121  阅读(7)  评论(0)    收藏  举报