首页 > 其他分享 >CSP2024 前集训:多校A层冲刺NOIP2024模拟赛08

CSP2024 前集训:多校A层冲刺NOIP2024模拟赛08

时间:2024-10-20 21:32:05浏览次数:6  
标签:unlocked int 08 多校 Tp CSP2024 read inline void

前言

image

先痛骂没良心出题人,T1 \(n\sqrt n\) 多大你刚好给多大,一点不多给,T2 才是签到题,因为放了 T2 位置打了暴力就去想 T3 了,我是唐氏,谁让你 T1、T2 swap 的?T3 实则三道题。

但是还是感觉 T1 更简单啊,\(5e4\) 搁哪儿摆着呢一眼 \(O(n\sqrt n)\),甚至空间也是这么多,太明显了。

挂分挂上天了,T1 本来是首切,第一发直接过了,但是因为排序多个 \(\log\) 怕过不去加了个桶排,结果空间刚好炸,挂成 \(50\),然后因为赛时曾经过了赛后再过甚至不显示,但实际上存图的时候直接用 vector 下表为权值就是排好序的不需要再排序,属于脑瘫了,T3 空间开小暴力分全无哈哈。

T1 排列最小生成树

显然有每个边权都 \(\le n\)。

  • 证明:

    对于每个点,其位置相邻或权值相邻的点与他的边权一定 \(\le n\)。

    点 \(i\) 连接 \(i-1\),构成一条链,即一定存在一组所有权值都 \(\le n\) 的合法方案。

    若存在一条边边权 \(>n\),不妨断开这条边,分成两个大小分别为 \(n,m\) 的连通块,根据鸽巢原理,\(n\times m\) 条边中一定存在一条权值 \(\le n\) 的边,连该条即可,从而存在边权 \(>n\) 的方案一定不是最优解。

    证毕。

从而对于一个点两边各扫 \(\sqrt{n}\) 即可(位置和权值),从而边数为不会超过 \(2n\sqrt n\)(没有判 \((x,y)=(y,x)\)),再跑最小生成树即可,注意建图方式(前言中已说)。

点击查看代码
#include<bits/stdc++.h>
#define ll long long
#define endl '\n'
#define sort stable_sort
#define pb push_back
#define mkp make_pair
#define fi first
#define se second
using namespace std;
const int N=5e4+10;
template<typename Tp> inline void read(Tp&x)
{
	x=0;register bool z=true;
	register char c=getchar_unlocked();
	for(;!isdigit(c);c=getchar_unlocked()) if(c=='-') z=0;
	for(;isdigit(c);c=getchar_unlocked()) x=(x<<1)+(x<<3)+(c^48);
	x=(z?x:~x+1);
}
template<typename T,typename ...Tp> inline void read(T &x,Tp &...y){read(x);read(y...);}
template<typename Tp> inline void wt(Tp x){if(x>9)wt(x/10);putchar_unlocked((x%10)+'0');}
template<typename Tp> inline void write(Tp x){if(x<0)putchar_unlocked('-'),x=~x+1;wt(x);}
template<typename T,typename ...Tp> inline void write(T x,Tp ...y){write(x);putchar_unlocked(' ');write(y...);}
int n,t,tot,a[N],f[N],id[N]; ll ans,tmp; vector<pair<int,int> >e[N];
inline int find(int x) {return x==f[x]?x:f[x]=find(f[x]);}
signed main()
{
	freopen("pmst.in","r",stdin),freopen("pmst.out","w",stdout);
	read(n),t=sqrt(n); bool flag=0;
	for(int i=1;i<=n;i++) read(a[i]),id[a[i]]=i,f[i]=i,flag|=(a[i]!=i);
	if(!flag) return write(n-1),0;
	for(int i=1;i<=n;i++)
	{
		for(int j=i-1;j>=max(1,i-t);j--)
			if((tmp=1ll*abs(a[i]-a[j])*(i-j))<=n) e[tmp].pb(mkp(i,j));
		for(int j=i+1;j<=min(n,i+t);j++)
			if((tmp=1ll*abs(a[i]-a[j])*(j-i))<=n) e[tmp].pb(mkp(i,j));
		for(int j=a[i]-1;j>=max(1,a[i]-t);j--)
			if((tmp=1ll*(a[i]-j)*abs(i-id[j]))<=n&&abs(i-id[j])>t) 
				e[tmp].pb(mkp(i,id[j]));
		for(int j=a[i]+1;j<=min(n,a[i]+t);j++)
			if((tmp=1ll*(j-a[i])*abs(i-id[j]))<=n&&abs(i-id[j])>t) 
				e[tmp].pb(mkp(i,id[j]));
	}
	for(int i=1,x,y;i<=n;i++) for(auto j:e[i])
	{
		if((x=find(j.fi))==(y=find(j.se))) continue;
		f[x]=y,ans+=i; if(++tot==n-1) return write(ans),0;
	}
}

T2 卡牌游戏

每个 \(a_i\) 对应的所有 \(b_j\) 是固定的,定义 \(g=\gcd(n,m)\),也就是说进行了 \(g\) 各循环,根据裴蜀定理 \(a_i\) 对应的所有 \(b_j\) 满足 \(i≡j\pmod p\),从而对于每个 \(g\) 的余数分别排序双指针就做完了。

点击查看代码
#include<bits/stdc++.h>
#define ll long long
#define endl '\n'
#define sort stable_sort
using namespace std;
const int N=1e5+10;
template<typename Tp> inline void read(Tp&x)
{
	x=0;register bool z=true;
	register char c=getchar_unlocked();
	for(;!isdigit(c);c=getchar_unlocked()) if(c=='-') z=0;
	for(;isdigit(c);c=getchar_unlocked()) x=(x<<1)+(x<<3)+(c^48);
	x=(z?x:~x+1);
}
template<typename T,typename ...Tp> inline void read(T &x,Tp &...y){read(x);read(y...);}
template<typename Tp> inline void wt(Tp x){if(x>9)wt(x/10);putchar_unlocked((x%10)+'0');}
template<typename Tp> inline void write(Tp x){if(x<0)putchar_unlocked('-'),x=~x+1;wt(x);}
template<typename T,typename ...Tp> inline void write(T x,Tp ...y){write(x);putchar_unlocked(' ');write(y...);}
int n,m,g,a[N],b[N]; ll ans1,ans2; vector<int>va,vb;
inline int gcd(int x,int y) {return !y?x:gcd(y,x%y);}
signed main()
{
	freopen("cardgame.in","r",stdin),freopen("cardgame.out","w",stdout);
	read(n,m),g=gcd(n,m);
	for(int i=1;i<=n;i++) read(a[i]);
	for(int i=1;i<=m;i++) read(b[i]);
	for(int i=0;i<g;i++,va.clear(),vb.clear())
	{
		for(int j=(!i)*g;j+i<=n;j+=g) va.push_back(a[j+i]);
		for(int j=(!i)*g;j+i<=m;j+=g) vb.push_back(b[j+i]);
		sort(va.begin(),va.end()),sort(vb.begin(),vb.end());
		for(int j=0,k=0,sum1=0,sum2=0;j<va.size();j++)
		{
			if(j&&va[j]>va[j-1]) sum1+=sum2,sum2=0;
			for(;k<vb.size()&&vb[k]<va[j];k++) sum1++;
			for(;k<vb.size()&&vb[k]==va[j];k++) sum2++;
			ans1+=sum1,ans2+=sum2;
		}
	}
	write(ans1*=g),puts(""),write(1ll*n*m-ans1-(ans2*=g)),puts(""),write(ans2);
}

T3 比特跳跃

  • \(s=1\):

    若 \(n\) 为 \(2\) 的整数次幂,则 \(1\sim n-1\) 直接到 \(n\) 的代价都是 \(0\)。

    否则找到最大的一个 \(i=2^j-1\),那么 \(1\sim i-1\) 到 \(i\) 的代价都是 \(0\),大于 \(i\) 的数可以先调到他的补再跳回来,还是 \(0\),只有 \(2^{j+1}-1\) 例外,特判掉即可。

  • \(s=2\):

    跳跃的代价至于不同的 \(1\) 的个数有关,从而 \(x\to x ⊕ 2^{i}+x\to x ⊕ 2^{j}=x\to x ⊕ 2^i ⊕ 2^j\),从而只需要连 \(O(n\log(n))\) 条边,跑最短路即可。

  • \(s=3\):

    跳两次一定不如跳一次,所以 \(i\) 要么直接从 \(1\) 跳过来,要么从 \(i\) 的子集跳过来(代价为 \(i\)),从而先跑一边最短路,再用子集更新答案再跑一边最短路即可。

点击查看代码
#include<bits/stdc++.h>
#define ll long long
#define endl '\n'
#define sort stable_sort
using namespace std;
const int N=1e5+10,M=3.5e6+10;
template<typename Tp> inline void read(Tp&x)
{
	x=0;register bool z=true;
	register char c=getchar_unlocked();
	for(;!isdigit(c);c=getchar_unlocked()) if(c=='-') z=0;
	for(;isdigit(c);c=getchar_unlocked()) x=(x<<1)+(x<<3)+(c^48);
	x=(z?x:~x+1);
}
template<typename T,typename ...Tp> inline void read(T &x,Tp &...y){read(x);read(y...);}
template<typename Tp> inline void wt(Tp x){if(x>9)wt(x/10);putchar_unlocked((x%10)+'0');}
template<typename Tp> inline void write(Tp x){if(x<0)putchar_unlocked('-'),x=~x+1;wt(x);}
template<typename T,typename ...Tp> inline void write(T x,Tp ...y){write(x);putchar_unlocked(' ');write(y...);}
int n,m,s,tot,head[N],nxt[M],to[M]; ll k,f[N],w[M],dis[N]; bitset<N>vis;
priority_queue<pair<ll,int> >q;
void add(int x,int y,ll z)
{nxt[++tot]=head[x],to[tot]=y,w[tot]=z,head[x]=tot;}
void init1()
{memset(dis,0x3f,sizeof(dis)),dis[1]=0,q.push(make_pair(0,1));}
void init2()
{for(int i=1;i<=n;i++) q.push(make_pair(-dis[i],i)); vis.reset();}
void dijkstra()
{
	while(!q.empty())
	{
		int x=q.top().second; q.pop(); if(vis[x]) continue; vis.set(x);
		for(int i=head[x],y;~(y=to[i]);i=nxt[i]) if(dis[y]>dis[x]+w[i])
			dis[y]=dis[x]+w[i],q.push(make_pair(-dis[y],y));
	}
}
signed main()
{
	freopen("jump.in","r",stdin),freopen("jump.out","w",stdout);
	read(n,m,s,k); to[0]=-1;
	if(s==1&&n==(1<<(__lg(n)+1))-1)
	{
		dis[n]=1e9; for(int i=1;i<n;i++) dis[n]=min(dis[n],k*(i&n));
		for(int i=1,x,y,z;i<=m;i++)
			read(x,y,z),(x==n||y==n)&&(dis[n]=min(dis[n],(ll)z));
	}
	else if(s==2)
	{
		for(int i=1;i<=n;i++) for(int j=0;j<=__lg(n);j++)
			if((i>>j)&1) add(i,i^(1<<j),k*(1<<j)),add(i^(1<<j),i,k*(1<<j));
		for(int i=1,x,y,z;i<=m;i++) read(x,y,z),add(x,y,z),add(y,x,z);
		init1(),dijkstra();
	}
	else if(s==3)
	{
		for(int i=2;i<=n;i++) add(1,i,k*(i|1)),add(i,1,k*(i|1));
		for(int i=1,x,y,z;i<=m;i++) read(x,y,z),add(x,y,z),add(y,x,z);
		init1(),dijkstra(),memset(f,0x3f,sizeof(f)),f[1]=0;
		for(int i=2;i<=n;i++) for(int j=0;j<=__lg(n);j++) if((i>>j)&1)
			f[i]=min((dis[i]=min(dis[i],f[i^(1<<j)]+k*i)),f[i^(1<<j)]);
		init2(),dijkstra();
	}
	for(int i=2;i<=n;i++) write(dis[i]),putchar_unlocked(' ');
}

T4 区间

  • 部分分 \(35pts\):二维前缀和直接做。

  • 部分分 \(60pts\):二维偏序,扫描线,对于 \(a_i\le 3,b_i\le 3\) 复杂度是对的。

  • 正解:

    线段树维护历史和板子,限制一可以单调栈维护,限制二可以单调栈里跑二分,因为没有强制在线,所以直接扫描线即可。

    点击查看代码
    #include<bits/stdc++.h>
    #define ll long long
    #define endl '\n'
    #define sort stable_sort
    #define mid (l+r>>1)
    #define ls (mid<<1)
    #define rs (mid<<1|1)
    #define pb push_back
    #define mkp make_pair
    #define fi first
    #define se second
    using namespace std;
    const int N=3e5+10;
    template<typename Tp> inline void read(Tp&x)
    {
        x=0;register bool z=true;
        register char c=getchar_unlocked();
        for(;!isdigit(c);c=getchar_unlocked()) if(c=='-') z=0;
        for(;isdigit(c);c=getchar_unlocked()) x=(x<<1)+(x<<3)+(c^48);
        x=(z?x:~x+1);
    }
    template<typename T,typename ...Tp> inline void read(T &x,Tp &...y){read(x);read(y...);}
    template<typename Tp> inline void wt(Tp x){if(x>9)wt(x/10);putchar_unlocked((x%10)+'0');}
    template<typename Tp> inline void write(Tp x){if(x<0)putchar_unlocked('-'),x=~x+1;wt(x);}
    template<typename T,typename ...Tp> inline void write(T x,Tp ...y){write(x);putchar_unlocked(' ');write(y...);}
    int n,m,a[N],b[N],sta[N],sum[N<<1],tag[N<<1]; ll ans[N],hsum[N<<1];
    vector<pair<int,int> >e[N];
    inline void spread(int p,int l,int r)
    {
        hsum[ls]+=1ll*sum[ls]*tag[p],tag[ls]+=tag[p];
        hsum[rs]+=1ll*sum[rs]*tag[p],tag[rs]+=tag[p],tag[p]=0;
    }
    void add(int p,int l,int r,int vl,int vr)
    {
        if(vl<=l&&vr>=r) return hsum[p]+=sum[p],tag[p]++,void();
        spread(p,l,r);
        if(vl<=mid) add(ls,l,mid,vl,vr);
        if(vr>mid) add(rs,mid+1,r,vl,vr);
        sum[p]=sum[ls]+sum[rs],hsum[p]=hsum[ls]+hsum[rs];
    }
    void change(int p,int l,int r,int x,int d)
    {
        if(l==r) return sum[p]+=d,void();
        spread(p,l,r),x<=mid?change(ls,l,mid,x,d):change(rs,mid+1,r,x,d);
        sum[p]=sum[ls]+sum[rs],hsum[p]=hsum[ls]+hsum[rs];
    }
    ll ask(int p,int l,int r,int vl,int vr)
    {
        if(vl<=l&&vr>=r) return hsum[p];
        spread(p,l,r); ll res=0;
        if(vl<=mid) res+=ask(ls,l,mid,vl,vr);
        if(vr>mid) res+=ask(rs,mid+1,r,vl,vr);
        return res;
    }
    signed main()
    {
        freopen("interval.in","r",stdin),freopen("interval.out","w",stdout);
        read(n),change(1,1,n,sta[1]=1,1);
        for(int i=1;i<=n;i++) read(a[i]); for(int i=2;i<=n;i++) read(b[i]);
        read(m); for(int i=1,l,r;i<=m;i++) read(l,r),e[r].pb(mkp(l,i));
        for(int i=2,top=1,pos,l,r;i<=n;i++)
        {
            for(pos=0,l=1,r=top;l<=r;) a[sta[mid]]<b[i]?r=(pos=mid)-1:l=mid+1;
            if(pos) add(1,1,n,sta[pos],i);
            for(auto it:e[i]) ans[it.se]=ask(1,1,n,it.fi,i);
            for(;top&&a[sta[top]]<=a[i];) change(1,1,n,sta[top--],-1);
            change(1,1,n,sta[++top]=i,1);
        }
        for(int i=1;i<=m;i++) write(ans[i]),puts("");
    }
    

标签:unlocked,int,08,多校,Tp,CSP2024,read,inline,void
From: https://www.cnblogs.com/Charlieljk/p/18487960

相关文章

  • 408数据结构-折半查找,分块查找 自学知识点整理
    前置知识:查找的基本概念折半查找折半查找又称二分查找,它仅适用于有序的顺序表。因个人习惯,下文均用二分代替折半。二分查找的基本思想:首先将给定值ke......
  • 多校A层冲刺NOIP2024模拟赛09
    多校A层冲刺NOIP2024模拟赛09考试唐完了,T2、T4都挂了100分,人麻了。排列最小生成树给定一个\(1,2,\dots,n\)的排列\(p_1,p_2,\dots,p_n\)。构造一个\(n\)个点的完全无向图,节点编号分别是\(1,2,\dots,n\)。节点i和节点j之间的边边权为\(|pi−pj|×|i......
  • 2024-2025-1 20241308 《计算机基础与程序设计》第四周学习总结
    作业信息这个作业属于哪个课程 https://edu.cnblogs.com/campus/besti/2024-2025-1-CFAP这个作业要求在哪里 https://www.cnblogs.com/rocedu/p/9577842.html#WEEK04这个作业的目标 <门电路组合电路,逻辑电路冯诺依曼结构CPU,内存,IO管理嵌入式系统,并行结构物理安全>作业正......
  • Linux学习笔记(复习版day008)
    1.僵尸进程僵尸进程(ZombieProcess)是指那些已经终止(即完成执行)的进程,但其父进程尚未读取其退出状态信息的进程。简单来说,僵尸进程的生命周期已经结束,但它的进程描述符仍然存在于系统中,以便父进程能够获取其退出状态。处理:1.top命令查询是否有僵尸进程,此处1zombie表示有一个......
  • # 2024-2025-1 20241408陈烨南 《计算机基础与程序设计》第4周学习总结
    2024-2025-120241408陈烨南《计算机基础与程序设计》第4周学习总结作业信息这个作业属于哪个课程<班级的链接>(如2024-2025-1-计算机基础与程序设计)这个作业要求在哪里<作业要求的链接>(如2024-2025-1计算机基础与程序设计第一周作业)这个作业的目标门电路,组合电......
  • csp2024 游记
    前言今年赛前这段时间或许算得上是我OI生涯中最摆的一段时间了。每天到机房无非就是胡乱的看一些题,趴在桌子上,或者是写whk作业。当然这可能也和最近的状态有关。或许我也只有在刚开始的那几天的在线的,其它时候基本上啥都没干。本来今年是不太想停课的,可最终因为种种原因还是......
  • 『模拟赛』多校A层冲刺NOIP2024模拟赛09
    Rank还行A.排列最小生成树(pmst)签,有点可惜。考虑连\(i\)与\(i+1\)时,所有边边权都是小于\(n\)的,因此我们只考虑边权小于\(n\)的边即可。因为边权为\(|p_i-p_j|\times|i-j|\),所以只考虑\(|p_i-p_j|\lt\sqrt{n}\)和\(|i-j|\lt\sqrt{n}\)的情况,每个点只用连......
  • 「模拟赛」多校 A 层联训 8
    A.排列最小生成树(pmst)大家都没签上的签到调参调到110能过?!但赛时用set这个大常数选手存的边,挂了个log,所以跑不动大样例。正解:发现只按把编号相邻的点连边构成一条链,此时所有边的长度都\(\len\),所以无论如何我们都能保证最小生成树每条边的边权\(\len\)。那么我们......
  • 多校A层冲刺NOIP2024模拟赛09
    又双叒叕垫底啦rank4,T150,T2100,T339,T435。accoder上同分,rank20排列最小生成树(pmst)打的\(O(n^2\logn^2)\)暴力发现总是存在⼀颗⽣成树,使得⽣成树⾥的所有边的边权都⼩于\(n\)。考虑Kruskal的过程,我们只需要留下那些边权⼩于\(n\)的边。然后用桶排序即可。点......
  • 多校A层冲刺NOIP2024模拟赛09
    GG多校A层冲刺NOIP2024模拟赛09T1排列最小生成树(pmst)需要思考一会。你得发现一个性质:所有要的边的权值都得小于n,因为每个点都可以找到至少一条边权小于n的边,所以最后生成树里的边的边权一定小于n。那么$\vertp_i-p_j\vert\times\verti-j\vert$中较......