[49 & 50] (多校联训) A层冲刺NOIP2024模拟赛08 | CSP-S 模拟 12

一小孩在奶茶店玩封盖机被绞断四根手指

记者:你现在感觉怎么样
小孩:👍

不是哥们 P 的,你可以自己去 hdk吧 找

我左手中指指甲里莫名其妙卡了个木刺

医生 1:(打手电筒)
医生 2:(尝试把刺弄出来)

医生 2:诶呀,断了

医生 2:你就这么想拔这个刺吗
我:这不拔能行?
医生 2:你看你这个也不深,长一周自己就掉了
我:不会发炎吗
医生 1:发炎了那不更好弄出来了吗,而且到时候发炎我们就能处理了,你现在这玩意我们也处理不了啊
医生 2:你让它自求多福去就行了
我:???

Ciallo~(∠・ω< )⌒★

咱们集资,到秦皇岛给 CTH 买条裙子,意下如何

[48] C.距离

补一下式子

max(|aiaj|,|bibj|)=|ai+bi2aj+bj2|+|aibi2ajbj2|=|(aiaj)+(bibj)2|+|(aiaj)(bibj)2|={(aiaj)+(bibj)2+(aiaj)(bibj)2=aiajaiajbibj(aiaj)+(bibj)2+(bibj)(aiaj)2=bibjotherwise

min(|aiaj|,|bibj|)=|aiaj|+|bibj|max(|aiaj|,|bibj|)=aiaj|+|bibj||ai+bi2aj+bj2||aibi2ajbj2|

[49] A.传送

首先发现横坐标或纵坐标相等的点之间移动不需要费用,因此考虑对横纵坐标拉出来建虚点

然后发现其实不用对横纵坐标建虚点,只需要把横坐标或纵坐标相邻的点连边即可

所以排序后直接连

正常边正常连即可

#include<bits/stdc++.h>
using namespace std;
int n,m;
struct edge{
    int to,w;
};
vector<edge>e[200001];
int x[200001],y[200001];
int id[200001];
inline static int cal(int i,int j)noexcept{
    return min(abs(x[i]-x[j]),abs(y[i]-y[j]));
}
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;
inline 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]});    
            }
        }
    }
}
int main(){
    scanf("%d %d",&n,&m);
    for(int i=1;i<=n;++i){
        scanf("%d %d",&x[i],&y[i]);
    }
    iota(id+1,id+n+1,1);
    sort(id+1,id+n+1,[](int a,int b){return x[a]<x[b];});
    for(int i=2;i<=n;++i){
        e[id[i-1]].push_back({id[i],cal(id[i],id[i-1])});
        e[id[i]].push_back({id[i-1],cal(id[i],id[i-1])});
    }
    sort(id+1,id+n+1,[](int a,int b){return y[a]<y[b];});
    for(int i=2;i<=n;++i){
        e[id[i-1]].push_back({id[i],cal(id[i],id[i-1])});
        e[id[i]].push_back({id[i-1],cal(id[i],id[i-1])});
    }
    for(int i=1;i<=m;++i){
        int x,y,z;scanf("%d %d %d",&x,&y,&z);
        e[x].push_back({y,z});
        e[y].push_back({x,z});
    }
    dij(1);
    for(int i=2;i<=n;++i) cout<<dis[i]<<" ";
}

[49] B.排列

如果两个数 a,b 满足 gcd(a,b)=k,则一定会有 a=m1k,b=m2k, (m1,m2Z)

所以只有 iknki 对答案有用,而题目保证 nk10

因此状压,fi,j,k 设选到 i,当前在 nk 个数中的的选数状态为 j,上一个选了 k 的方案数(注意到选不在 nk 中的数的本质都是一样的,因此统一视作 0

或者记搜亦可

#include<bits/stdc++.h>
using namespace std;
const int p=998244353;
int f[3001][1<<10][11];
int n,k,tot;
int maxn;
long long dfs(int now,int choose,int lastchoose){
    if(now>n) return (choose==maxn);
    if(f[now][choose][lastchoose]!=-1) return f[now][choose][lastchoose];
    int tmp=__builtin_popcount(choose);
    if(n-now+1<tot-tmp) return 0;
    long long res=0;
    for(int i=0;i<=tot-1;++i){
        if((choose&(1<<i))==0){
            if(lastchoose==0 or __gcd(k*lastchoose,k*(i+1))!=k){
                res+=dfs(now+1,choose|(1<<i),i+1);            
                res%=p;
            }
        }
    }
    res+=dfs(now+1,choose,0);
    res%=p;
    return f[now][choose][lastchoose]=res;
}
long long fact(long long n){
    long long ans=1;
    for(int i=2;i<=n;++i) ans=ans*i%p;
    return ans;
}
signed main(){
    memset(f,-1,sizeof f);
    cin>>n>>k;
    tot=n/k;
    maxn=(1<<tot)-1;
    cout<<dfs(1,0,0)*fact(n-tot)%p;
}

[49] C.战场模拟器

  • 护盾与死亡不会太多,只要遇到直接暴力处理即可

  • 线段树维护当前区间最小非负血量及其数量(濒死的计数时用),护盾总数,死亡总数

  • 扣血:只要遇到存在护盾或者即将有人死亡的区间(可以通过区间最小值判断)就暴力递归,否则区间减

  • 回血:直接区间加即可

  • 加护盾:线段树单点修改

  • 查询死亡:直接区间查询

  • 查询濒死:即查询最小值为 0 的区间计数总和,仍然是区间查询

  • 注意人死后直接赋成极大值,不能太小,防止整体扣血之后再死一次,也不能太大,防止回血回炸了

从昨天下午调到现在,以为线段树错了,但是线段树一点错没有,错在极大值了

#include<bits/stdc++.h>
using namespace std;
#define int long long
int n,q;
int a[200001];
struct tree{
    int l,r;
    int deadcnt;
    int minn,minncnt;
    int lazy;
    int safe;
}t[800001];
#define tol (id*2)
#define tor (id*2+1)
#define mid(l,r) mid=((l)+(r))/2
void update(int id){
    if(t[tol].minn==t[tor].minn){
        t[id].minn=t[tol].minn;
        t[id].minncnt=t[tol].minncnt+t[tor].minncnt;
    }
    else if(t[tol].minn<t[tor].minn){
        t[id].minn=t[tol].minn;
        t[id].minncnt=t[tol].minncnt;
    }
    else{
        t[id].minn=t[tor].minn;
        t[id].minncnt=t[tor].minncnt;
    }
    t[id].deadcnt=t[tol].deadcnt+t[tor].deadcnt;
    t[id].safe=t[tol].safe+t[tor].safe;
}
void build(int id,int l,int r){
    t[id].l=l;t[id].r=r;
    if(l==r){
        t[id].minn=a[l];
        t[id].minncnt=1;
        return;
    }
    int mid(l,r);
    build(tol,l,mid);
    build(tor,mid+1,r);
    update(id);
}
void pushdown(int id){
    if(t[id].lazy){
        t[tol].lazy+=t[id].lazy;
        t[tor].lazy+=t[id].lazy;
        t[tol].minn+=t[id].lazy;
        t[tor].minn+=t[id].lazy;
        t[id].lazy=0;
    }
}
void change(int,int,int,int);
void bl_change(int id,int l,int r,int val){
    if(t[id].l==t[id].r){
        if(t[id].deadcnt);
        else if(val<0 and t[id].safe) t[id].safe--;
        else{
            t[id].minn+=val;
            if(t[id].minn<0){
                t[id].deadcnt=1;
                t[id].minn=1e18;
                t[id].minncnt=0;
            }
        }
        return;
    }
    pushdown(id);
    if(t[tol].safe or t[tol].minn+val<0) bl_change(tol,l,t[tol].r,val);
    else change(tol,l,t[tol].r,val);
    if(t[tor].safe or t[tor].minn+val<0) bl_change(tor,t[tor].l,r,val);
    else change(tor,t[tor].l,r,val);
    update(id);
}
void change(int id,int l,int r,int val){
    if(l<=t[id].l and t[id].r<=r){
        if(val>=0 or (t[id].safe==0 and t[id].minn+val>=0)){
            t[id].lazy+=val;
            t[id].minn+=val;
            return;
        }
        bl_change(id,l,r,val);
        return;
    }
    pushdown(id);
    if(r<=t[tol].r) change(tol,l,r,val);
    else if(l>=t[tor].l) change(tor,l,r,val);
    else{
        change(tol,l,t[tol].r,val);
        change(tor,t[tor].l,r,val);
    }
    update(id);
}
void change_safe(int id,int p,int val){
    if(t[id].l==t[id].r){
        if(!t[id].deadcnt){
            t[id].safe++;
        }
        return;
    }
    pushdown(id);
    if(p<=t[tol].r) change_safe(tol,p,val);
    else change_safe(tor,p,val);
    update(id);
}
int askdead(int id,int l,int r){
    if(l<=t[id].l and t[id].r<=r){
        return t[id].deadcnt;
    }
    pushdown(id);
    if(r<=t[tol].r) return askdead(tol,l,r);
    else if(l>=t[tor].l) return askdead(tor,l,r);
    else return askdead(tol,l,t[tol].r)+askdead(tor,t[tor].l,r);
}
int askdying(int id,int l,int r){
    if(l<=t[id].l and t[id].r<=r){
        if(t[id].minn==0){
            return t[id].minncnt;
        }
        return 0;
    }
    pushdown(id);
    if(r<=t[tol].r) return askdying(tol,l,r);
    else if(l>=t[tor].l) return askdying(tor,l,r);
    else return askdying(tol,l,t[tol].r)+askdying(tor,t[tor].l,r);
}
signed main(){
    freopen("simulator.in","r",stdin);
    freopen("simulator.out","w",stdout);
    scanf("%lld",&n);
    for(int i=1;i<=n;++i){
        scanf("%lld",&a[i]);
    }
    build(1,1,n);
    scanf("%lld",&q);
    while(q--){
        int opt;scanf("%lld",&opt);
        if(opt==1){
            int l,r,x;scanf("%lld %lld %lld",&l,&r,&x);
            change(1,l,r,-x);
        }
        if(opt==2){
            int l,r,x;scanf("%lld %lld %lld",&l,&r,&x);
            change(1,l,r,x);
        }
        if(opt==3){
            int h;scanf("%lld",&h);
            change_safe(1,h,1);
        }
        if(opt==4){
            int l,r;scanf("%lld %lld",&l,&r);
            printf("%lld\n",askdead(1,l,r));
        }
        if(opt==5){
            int l,r;scanf("%lld %lld",&l,&r);
            printf("%lld\n",askdying(1,l,r));
        }
    }
}

[50] A.小 h 的几何

  • 三角形三边的中点、三条高的垂足、三角形三个顶点与垂心连线的中点共九个点共圆

  • 三角形 {(x1,y1),(x2,y2),(x3,y3)} 唯一确定的符合上述要求的圆的圆心为 (x1+x2+x32,y1+y2+y32)

第一条挺神奇的,先不证明了,晚上有空补

第二条的证明:

对不起,脑子锈住了,不会几何,只能这么证了

设中点 D(x1+x22,y1+y22),E(x1+x32,y1+y32),F(x2+x32,y2+y32)

midDE(2x1+x2+x34,2y1+y2+y34),midEF(x1+x2+2x34,y1+y2+2y34)

lDE:y=y2y3x2x3x+y1+y22x1+x2x2x3×y2y32

lEF:y=y1y2x1x2x+y1+y32x1+x3x1x2×y1y22

{1lDEmidDEl3:y=x2x3y2y3x+2y1+y2+y34x2x3y2y3×2x1+x2+x34{1lEFmidEFl4:y=x1x2y1y2x+y1+y2+2y34x1x2y1y2×x1+x2+2x34

联立

x=y1+y2+2y34x1x2y1y2×x1+x2+2x342y1+y2+y34+x2x3y2y3×2x1+x2+x34x2x3y2y3x1x2y1y2=(y3y1)(y1y2)(y2y3)(x1x2)(y2y3)(x1+x2+2x3)+(y1y2)(x2x3)(2x1+x2+x3)4[(y1y2)(x2x3)(y2y3)(x1x2)]=y1x1x2y1x1x3x12y2x12y3+y1x22+x2x3y2y2x1x2+x22y3y1x32+x32y2x1y3x3+x2x3y32(y1x2y1x3+x3y2y2x1x1y3+x2y3)=x1+x2+x32

所以直接统计每个数在和式的出现次数,发现全部是定值 (n1)(n2)2,因此直接加和即可

代码里将 (n1)(n2)26n(n1)(n2) 提前做了一点约分

#include<bits/stdc++.h>
using namespace std;
const long double pi=acos(-1);
struct node{
    public:long double x,y;
    node(){}
    node(long double xp,long double yp){x=xp;y=yp;}
    node operator +(node A){
        return node(x+A.x,y+A.y);
    }
    node operator +=(node A){
        return *this=*this+A;
    }
    node operator *(long double dx){
        return node(x*dx,y*dx);
    }
    node operator *=(long double dx){
        return *this=*this*dx;
    }
};
int n;
node a[500001];
long long theta;
node ans;

int main(){
    scanf("%d",&n);
    for(int i=1;i<=n;++i){
        scanf("%lld",&theta);
        a[i].x=cos(theta*pi/1e9);
        a[i].y=sin(theta*pi/1e9);
        ans+=a[i];
    }
    ans*=(3.0/(n*2.0));
    printf("%.20Lf %.20Lf",ans.x,ans.y);
}
这是什么

posted @   HaneDaniko  阅读(62)  评论(3编辑  收藏  举报
相关博文:
阅读排行:
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 单线程的Redis速度为什么快?
· SQL Server 2025 AI相关能力初探
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 展开说说关于C#中ORM框架的用法!
点击右上角即可分享
微信分享提示