首页 > 其他分享 >[51] (多校联训) A层冲刺NOIP2024模拟赛09

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

时间:2024-10-19 17:32:52浏览次数:8  
标签:gcd int 09 51 sqrt vis 联训 id dis

关于生成式 AI

怎么才能让这个 b 学会断句

我目前的方案是,把逗号和句号单独作为一个特殊词汇看待,也统计到词频里,该断句的时候就断

表扬这次的题解,写的很清楚

A.排列最小生成树

  • 总存在一颗生成树使得树上最大边权值小于 \(n\)

考虑直接连接序列里的所有 \((i,i+1)\),因为 \(|a_i-a_{i+1}|\lt n\)(由排列的性质),因此 \(w_{\max}\lt((i+1)-i)\times n=n\)

所以我们直接考虑连权值小于 \(n\) 的边即可

  • 一条边 \((i,j)\lt n\),则 \(|i-j|\lt \sqrt{n}\) 或 \(|a_i-a_j|\lt\sqrt{n}\)

考虑反证,设 \(|i-j|\ge \sqrt{n}\) 且 \(|a_i-a_j|\ge \sqrt{n}\),考虑最小的情况也有 \(\sqrt{n}\times \sqrt{n}=n\ge n\),因此原命题成立

所以我们只需要考虑使得 \(|i-j|\lt \sqrt{n}\) 或 \(|a_i-a_j|\lt\sqrt{n}\) 的节点即可

因为是排列,可以提前统计每个值所在的位置,然后直接通过值或者位置加减来找对应的点

枚举的这里有个优化(没啥大用)

  • 由于是无序数对,钦定里层的变量小于外层变量,这样可以少一半枚举量和空间浪费

找到了以后直接跑最小生成树就行了

但是直接这么跑复杂度是 \(n\sqrt{n}\log n\alpha\) 的,还挂大常数,显然过不去

刚才我们已经证明了,我们找到的值的值域在 \(n\) 之内,因此我们直接对值域开桶,每次找到数对就存到对应的桶里,这样就能避免排序

复杂度 \(n\sqrt{n}\alpha\)

#include<bits/stdc++.h>
using namespace std;
struct dsu{
    int fa[50001];
    void clear(int n){
        for(int i=1;i<=n;++i){
            fa[i]=i;
        }
    }
    int find(int id){
        if(id==fa[id]) return id;
        return fa[id]=find(fa[id]);
    }
    bool connect(int x,int y){
        int fx=find(x),fy=find(y);
        if(fx==fy) return false;
        fa[fx]=fy;
        return true;
    }
}dsu;
int n;
int a[50001];
inline int sol(int i,int j){
    return abs(i-j)*abs(a[i]-a[j]);
}
struct node{
    int i,j;
};
vector<node>q[50001];
int pos[50001];
int vis[50001];
int main(){
    int st=scanf("%d",&n);
    for(int i=1;i<=n;++i){
        st=scanf("%d",&a[i]);
        pos[a[i]]=i;
    }
    dsu.clear(n);
    int tmp=sqrt(n);
    for(int i=1;i<=n;++i){
        for(int j=max(1,i-tmp);j<i;++j){
            if(sol(i,j)<=n){
                q[sol(i,j)].push_back({i,j});
            }
            if(sol(pos[i],pos[j])<=n){
                q[sol(pos[i],pos[j])].push_back({pos[i],pos[j]});
            }
        }
    }
    long long tot=0,ans=0;
    for(int i=1;i<=n;++i){
        for(node u:q[i]){
            if(tot==n-1) break;
            if(dsu.connect(u.i,u.j)){
                ans+=i;
                tot++;
            }
        }
    }
    cout<<ans<<endl;
}

B.卡牌游戏

先考虑互质怎么做

n=3 m=4
n 1 2 3 1 2 3 1 2 3 1 2 3
m 1 2 3 4 1 2 3 4 1 2 3 4

通过找规律可以发现,\(m_i=1\) 的所有位置中,对应的 \(n_i=1,2,3\) 恰好都出现过了一遍,其他的 \(m_i\) 也是同理,因此我们直接对 \(n\) 维护有序数组,对每个 \(m_i\),既然它们面对的对手都是一样的,那么也一样处理,在 \(n\) 的有序数组里二分查找大于,小于,等于 \(m_i\) 的数即可

那么不互质怎么做

n=4 m=6 gcd(n,m)=2
1 2 3 4 1 2 3 4 1 2 3 4 1 2 3 4 1 2 3 4 1 2 3 4
1 2 3 4 5 6 1 2 3 4 5 6 1 2 3 4 5 6 1 2 3 4 5 6

可以发现,\(m_i=1,3,5\) 对应了 \(n_i=1,3\),\(m_i=2,4,6\) 对应了 \(n_i=2,4\)

  • 如果 \(m_i\equiv n_j\pmod {\text{gcd}(n,m)}\),则 \(m_i\) 的对手中有 \(n_j\)

这里感性理解比较好理解,这里 \(i\) 和 \(j\) 可以理解成两个数组下标差(偏移量),如果两个数初始偏移量为 \(dx\),每次偏移量均会增加 \(|n-m|\),设 \(n=k_1\text{gcd}(n,m),m=k_2\text{gcd}(n,m)\),进行 \(g\) 轮后的偏移量为 \(g\times |n-m|+dx=g|k_1-k_2|\text{gcd}(n,m)+dx\),可以发现都是在模 \(\text{gcd}(n,m)\) 的意义下同余的

因此可以拆开,将 \(i\mod \text{gcd}(n,m)\) 的数分为同一组,直接像上面互质的情况那样分别跑就行了

#include<bits/stdc++.h>
using namespace std;
#define int long long
int n,m;
int a[100001],b[100001];
vector<int>v[100001];
int ans[3];
int gcd;
signed main(){
    scanf("%lld %lld",&n,&m);
    gcd=__gcd(n,m);
    for(int i=1;i<=n;++i){
        scanf("%lld",&a[i]);
        v[i%gcd].push_back(a[i]);
    }
    for(int i=0;i<=gcd-1;++i) sort(v[i].begin(),v[i].end());
    for(int i=1;i<=m;++i){
        scanf("%lld",&b[i]);
        ans[0]+=tmp*gcd;ans[1]+=tmp2*gcd;ans[2]+=(v[i%gcd].size()-tmp-tmp2)*gcd;
    }
    cout<<ans[1]<<'\n'<<ans[0]<<'\n'<<ans[2]<<'\n';
}

C.比特跳跃

\(S=1\)

考虑怎么从 \(1\) 完全不花费跳到任意点

  • 如果目标点的二进制末尾是 \(0\),直接从 \(1\) 跳就不会有任何花费
  • 如果目标点的二进制末尾是 \(1\),那么将其取反,反串的二进制末尾是 \(0\),可以从 \(1\) 跳到其反串,再从反串调到目标点,均不需要花费

但是唯一的特例是形如 \(11111\cdots\) 的点(发现只有当 \(n=2^k\) 的时候,\(2^k-1\) 会出现这种情况,否则可以直接从高位上的点跳走),因为它的反串是 \(0\),而我们并没有 \(0\) 这个节点,此时我们可以采用下面两种方式跳到这个点

  • 直接从 \(1\) 跳过去
  • 不用花费地跳到相邻点,通过相邻的连边直接走到目标点

取最小值即可

\(S=2\)

关于异或,可以考虑拆位

  • 枚举 \(i\),每次我们只异或 \(i\) 的其中一位,并且将异或结果与 \(i\) 连边(因为是无向图,为了避免重复连边,可以只枚举 \(i\) 中为 \(1\) 的二进制位,实测不这么做会炸空间)
  • 直接跑最短路即可

\(S=3\)

  • 如果 \(i\) 不是 \(j\) 的子集,\(1\rightarrow i\rightarrow j\) 一定不比 \(1\rightarrow j\) 优

因为你从 \(1\) 到 \(i\) 的贡献是 \(1\operatorname{or}i\),\(i\) 到 \(j\) 的贡献是 \(i\operatorname{or}j\),这两个加和一定包含了 \(1\operatorname{or}j\),并且还会多出来点

因此这么做

  • 先跑一遍最短路
  • 对每个 \(i\) 枚举其子集,尝试松弛 \(i\)
  • 再跑一遍最短路

这个枚举子集的复杂度太大,具体实现的时候,建一个数组 \(f_i\) 表示 \(i\) 及其子集的最小 \(dis\),然后枚举和 \(i\) 只差一个二进制位,并且还是 \(i\) 的子集的数(这些数一定比 \(i\) 小,如果你从小到大枚举 \(i\) 就可以保证这些数一定先被更新过了),用这些数的最小值对 \(i\) 进行松弛,同时记得也用这些数更新 \(f_i\),这样才能实现我们枚举所有子集的效果

要注意的点

  • 第二遍最短路前先把要松弛的点入队(或者你懒得判断直接入所有点也可以)
  • 初值,\(f_1=0\)
#include<bits/stdc++.h>
using namespace std;
#define int long long
int n,m,s,k;
struct edge{
    int to,w;
};
vector<edge>e[200001];
int dis[200001];
bool vis[200001];
struct node{
    int id,dis;
    bool operator <(const node&A)const{
        return dis>A.dis;
    }
};
priority_queue<node>q;
namespace subtask1{
    void dij(int s){
        memset(dis,0x3f,sizeof dis);
        memset(vis,0,sizeof vis);
        dis[s]=0;
        q.push({s,dis[s]});
        while(!q.empty()){
            node u=q.top();q.pop();
            if(vis[u.id]) continue;
            vis[u.id]=true;
            for(edge i:e[u.id]){
                if(dis[i.to]>dis[u.id]+i.w){
                    dis[i.to]=dis[u.id]+i.w;
                    q.push({i.to,dis[i.to]});
                }
            }
        }
    }
    void main(){
        for(int i=1;i<=m;++i){
            int x,y,z;int st=scanf("%lld %lld %lld",&x,&y,&z);
            e[x].push_back({y,z});
            e[y].push_back({x,z});
        }
        for(int i=1;i<=n;++i){
            for(int j=i+1;j<=n;++j){
                if(s==1){
                    e[i].push_back({j,k*(i&j)});
                    e[j].push_back({i,k*(i&j)});
                }
                if(s==2){
                    e[i].push_back({j,k*(i^j)});
                    e[j].push_back({i,k*(i^j)});
                }
                if(s==3){
                    e[i].push_back({j,k*(i|j)});
                    e[j].push_back({i,k*(i|j)});
                }
            }
        }
        dij(1);
        for(int i=2;i<=n;++i){
            cout<<dis[i]<<" ";
        }
        cout<<endl;
    }
}
bool ispowerof2(int x){
    if(x==1) return true;
    if(x%2!=0) return false;
    return ispowerof2(x/2);
}
namespace subtask2{
    void main(){
        for(int i=2;i<=n;++i){
            cout<<0<<" ";
        }
        cout<<endl;
    }
}
namespace subtask3{
    void main(){
        for(int i=1;i<=m;++i){
            int x,y,z;int st=scanf("%lld %lld %lld",&x,&y,&z);
            e[x].push_back({y,z});
            e[y].push_back({x,z});
        }
        int minn=0x7fffffff;
        for(edge i:e[n]) minn=min(minn,i.w);
        for(int i=2;i<=n-1;++i){
            cout<<0<<" ";
        }
        cout<<min(minn,k)<<" ";
        cout<<endl;
    }
}
namespace subtask4{
    void dij(int s){
        memset(dis,0x3f,sizeof dis);
        memset(vis,0,sizeof vis);
        dis[s]=0;
        q.push({s,dis[s]});
        while(!q.empty()){
            node u=q.top();q.pop();
            if(vis[u.id]) continue;
            vis[u.id]=true;
            for(edge i:e[u.id]){
                if(dis[i.to]>dis[u.id]+i.w){
                    dis[i.to]=dis[u.id]+i.w;
                    q.push({i.to,dis[i.to]});
                }
            }
        }
    }
    void main(){
        for(int i=1;i<=m;++i){
            int x,y,z;scanf("%lld %lld %lld",&x,&y,&z);
            e[x].push_back({y,z});
            e[y].push_back({x,z});
        }
        for(int i=1;i<=n;++i){
            for(int j=0;j<=31;++j){
                if((i&(1ll<<j))){
                    e[i].push_back({i^(1ll<<j),k*(1ll<<j)});
                    e[i^(1ll<<j)].push_back({i,k*(1ll<<j)});
                }
            }
        }
        dij(1);
        for(int i=2;i<=n;++i){
            cout<<dis[i]<<" ";
        }
    }
}
namespace subtask5{
    int f[200001];
    void dij(int s){
        memset(dis,0x3f,sizeof dis);
        memset(vis,0,sizeof vis);
        dis[s]=0;
        q.push({s,dis[s]});
        while(!q.empty()){
            node u=q.top();q.pop();
            if(vis[u.id]) continue;
            vis[u.id]=true;
            for(edge i:e[u.id]){
                if(dis[i.to]>dis[u.id]+i.w){
                    dis[i.to]=dis[u.id]+i.w;
                    q.push({i.to,dis[i.to]});
                }
            }
        }
    }
    void dij2(int s){
        memset(vis,0,sizeof vis);
        dis[s]=0;
        q.push({s,dis[s]});
        while(!q.empty()){
            node u=q.top();q.pop();
            if(vis[u.id]) continue;
            vis[u.id]=true;
            for(edge i:e[u.id]){
                if(dis[i.to]>dis[u.id]+i.w){
                    dis[i.to]=dis[u.id]+i.w;
                    q.push({i.to,dis[i.to]});
                }
            }
        }
    }
    void main(){
        for(int i=1;i<=m;++i){
            int x,y,z;scanf("%lld %lld %lld",&x,&y,&z);
            e[x].push_back({y,z});
            e[y].push_back({x,z});
        }
        for(int i=2;i<=n;++i){
            e[1].push_back({i,k*(1ll|i)});
            e[i].push_back({1,k*(1ll|i)});
        }
        dij(1);
        memset(f,0x3f,sizeof f);
        f[1]=0;
        for(int i=2;i<=n;++i){
            for(int j=0;j<=31;++j){
                if((i&(1ll<<j))){
                    dis[i]=min(dis[i],f[i^(1ll<<j)]+k*i);
                    f[i]=min(dis[i],f[i^(1ll<<j)]);
                }
            }
        }
        for(int i=2;i<=n;++i){
            q.push({i,dis[i]});
        }
        dij2(1);
        for(int i=2;i<=n;++i){
            cout<<dis[i]<<" ";
        }
    }
}
signed main(){
    freopen("jump.in","r",stdin);
    freopen("jump.out","w",stdout);
    // freopen("sample/jump/ex6.in","r",stdin);
    // freopen("out.out","w",stdout);
    int st=scanf("%lld %lld %lld %lld",&n,&m,&s,&k);
    if(n<=800 and m<=800){
        subtask1::main();
        return 0;
    }
    if(s==1 and ispowerof2(n+1)==false){
        subtask2::main();
        return 0;
    }
    if(s==1){
        subtask3::main();
        return 0;
    }
    if(s==2){
        subtask4::main();
        return 0;
    }
    if(s==3){
        subtask5::main();
        return 0;
    }
}

这是什么

初音未来可爱捏

标签:gcd,int,09,51,sqrt,vis,联训,id,dis
From: https://www.cnblogs.com/HaneDaCafe/p/18476062

相关文章

  • P1091 [NOIP2004 提高组] 合唱队形
    分析题目知道求一个最长上升子序列和一个最长下降子序列的长度均为同一个起点的最长的长度。于是求点击查看代码#include<iostream>#include<stack>#include<cmath>#include<algorithm>#include<set>#include<vector>#include<climits>#include<string.h>#incl......
  • CS 551 Systems Programming
    CS551SystemsProgramming,Fall2024ProgrammingProject1Out:10/13/2024Sun.Due:10/26/2024Sat.23:59:59Inthisprojectyouraregoingtoimplementacustommemorymanagerthatmanagesheapmemoryallocationatprogramlevel.Herearethereasonswh......
  • Golang笔记_day09
    Go面试题(二)1、怎么做代码优化减少内存分配        内存分配是任何程序的基本操作之一,也是一个明显的性能瓶颈。在Golang中,减少内存分配是一种有效的代码优化方式。为了减少内存分配,我们可以使用以下技巧:复用变量:在循环或迭代过程中,尽量避免重新分配变量。通过在循......
  • 2024.10.18 2309版
    起于《海奥华预言》的思考◆地球管理结构和参考持续更新中...... 英文地址:https://github.com/zhuyongzhe/Earth/tags中文地址:https://www.cnblogs.com/zhuyongzhe85作者:朱永哲 ---------------------------------------------------------------------------------......
  • P10992 Solution
    DescriptionLinkSolution好题。本题主要有两个问题:哈希函数的设计和子串的枚举。作为哈希题的套路,首先可以想到二分答案长度,再做check。考虑如何设计哈希函数来check。令二分出的长度为\(len\)。设计出的哈希函数需要满足两个条件:两个串对应的字符集相同,对应字符集的下......
  • 51单片机的超声波视力保护仪【proteus仿真+程序+报告+原理图+演示视频】
    1、主要功能 该系统由AT89C51/STC89C52单片机+LCD1602显示模块+温度传感器+光照传感器+超声波传感器+按键、LED、蜂鸣器等模块构成。适用于视力保护仪、坐姿矫正器、超声波防近视等相似项目。可实现功能:1、LCD1602显示温度、光照、距离和学习时间2、超声波传感器采集头......
  • 51单片机的超声波水位检测【proteus仿真+程序+报告+原理图+演示视频】
    1、主要功能 该系统由AT89C51/STC89C52单片机+LCD1602显示模块+超声波传感器+继电器+按键、LED、蜂鸣器等模块构成。适用于超声波液位监测与控制等相似项目。可实现功能:1、LCD1602显示水位信息2、超声波传感器采集水位信息3、如果水位低于阈值,声光报警,同时加水继电器......
  • jsp儿童疫苗接种管理系统q51zm(程序+源码+数据库+调试部署+开发环境)
    本系统(程序+源码+数据库+调试部署+开发环境)带论文文档1万字以上,文末可获取,系统界面在最后面。系统程序文件列表家长,接种人员,儿童信息,疫苗类型,疫苗信息,入库信息,出库信息,疫苗预约,接种信息,在线反馈,疫苗规划表开题报告内容一、项目背景随着公共卫生意识的提升,儿童......
  • 基于51单片机的智能台灯光照坐姿检测PWM红外无线手机蓝牙/WiFi/WiFi视频监控APP设计C0
    51单片机+人走灯灭+光敏AD采集+自动+手动+10档调节+坐姿监测+蜂鸣器+OLED屏/C01N51+蓝牙APP控制+人走灯灭+光敏AD采集+自动+手动+10档调节+坐姿监测+蜂鸣器+OLED屏/C01B51+WIFI-APP控制+人走灯灭+光敏AD采集+自动+手动+10档调节+坐姿监测+蜂鸣器+OLED屏/C01W51+视频监控+WIF......
  • 基于51单片机宠物自动喂食器定时时钟提醒加水水位无线手机蓝牙/WiFi/WiFi视频监控APP
    51单片机+时钟+校时+喂食+水位+加水喂水+三餐3定时+声光提醒+OLED屏+手动+自动/C16N51+蓝牙APP控制+时钟+校时+喂食+水位+喂水+三餐3定时+声光+OLED屏+手动+自动/C16B51+WIFI-APP控制+时钟+校时+喂食+水位+喂水+三餐3定时+声光+OLED屏+手动+自动/C16W51+视频监控+WIFI+时钟+......