2019.11.09【NOIP提高组】模拟 B 组

本文精选了解答JZOJ平台上四道算法竞赛题目的过程,包括偷懒的小X、渡河、菱形内的计数及电缆建设。文章深入分析了每道题目的解题思路,提供了高效的代码实现,涉及堆、线段树、SPFA算法、并查集及最小生成树等数据结构和算法知识。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

JZOJ 1402 偷懒的小X

题目

把一组数据按一定顺序依次插入数组中(即第 i i i个数为 a [ i ] a[i] a[i]),最后得出来就已经是一个堆,即不需要任何交换操作,若有多种方法,输出字典序最大的一组。满足 n = 2 x − 1 n=2^x-1 n=2x1


分析

一开始脑残考虑每层从大到小倒序输出,结果小了,后来我突然想起了线段树,考虑像线段树那样反向分治,就A了


代码

#include <cstdio>
#include <cctype>
#include <algorithm>
#define rr register
using namespace std;
int n,a[65601],rk[65601];
inline signed iut(){
	rr int ans=0,f=1; rr char c=getchar();
	while (!isdigit(c)) f=(c=='-')?-f:f,c=getchar();
	while (isdigit(c)) ans=(ans<<3)+(ans<<1)+(c^48),c=getchar();
	return ans*f;
}
inline void print(int ans){
	if (ans>9) print(ans/10);
	putchar(ans%10+48); 
}
inline void dfs(int x,int l,int r){
	rk[x]=l; if (l==r) return;
	rr int mid=(l+r+1)>>1;
	dfs(x<<1,mid+1,r);
	dfs(x<<1|1,l+1,mid);
}
signed main(){
	n=iut();
	for (rr int i=1;i<=n;++i) a[i]=iut();
	sort(a+1,a+1+n),dfs(1,1,n);
	for (rr int i=1;i<=n;++i) print(a[rk[i]]),putchar(i==n?10:32);
	return 0;
}

JZOJ 1403 渡河

代码(开O2AC的SPFA)

//SPFA理论O(n^4),实际开了O2快得飞起,正解缩点+bfs,但是懒得敲
#include <cstdio>
#include <cctype>
#include <queue>
#include <cstring>
#define rr register
using namespace std;
const int dx[8]={0,0,1,1,1,-1,-1,-1},dy[8]={1,-1,0,1,-1,0,1,-1};
struct rec{int x,y;}; int n,Q,dis[1011][1011];
bool a[1011][1011],v[1011][1011];
inline signed iut(){
	rr int ans=0; rr char c=getchar();
	while (!isdigit(c)) c=getchar();
	while (isdigit(c)) ans=(ans<<3)+(ans<<1)+(c^48),c=getchar();
	return ans;
}
inline void print(int ans){
	if (ans>9) print(ans/10);
	putchar(ans%10+48);
}
signed main(){
	n=iut(),Q=iut();
	memset(dis,42,sizeof(dis));
	for (rr int i=1;i<=n;++i)
	for (rr int j=1;j<=n;++j){
		rr char c=getchar();
		while (!isdigit(c)) c=getchar();
		a[i][j]=c^48;
	}
	rr queue<rec>q;
	for (rr int i=0;i<n+2;++i) q.push((rec){i,0}),v[i][0]=1,dis[i][0]=0;
	for (rr int i=0;i<n+2;++i) q.push((rec){i,n+1}),v[i][n+1]=1,dis[i][n+1]=0;
	for (rr int i=1;i<=n;++i) q.push((rec){0,i}),v[0][i]=1,dis[0][i]=0;
	for (rr int i=1;i<=n;++i) q.push((rec){n+1,i}),v[n+1][i]=1,dis[n+1][i]=0;
	while (q.size()){
		rr int qx=q.front().x,qy=q.front().y; q.pop();
		for (rr int k=0;k<8;++k){
			rr int nx=qx+dx[k],ny=qy+dy[k];
			if (nx<1||ny<1||nx>n||ny>n||dis[nx][ny]<=dis[qx][qy]+(!a[qx][qy]&&a[nx][ny])) continue;
			dis[nx][ny]=dis[qx][qy]+(!a[qx][qy]&&a[nx][ny]);
			if (!v[nx][ny]) v[nx][ny]=1,q.push((rec){nx,ny});
		}
		v[qx][qy]=0;
	}
    for (rr int x,y;Q;--Q) x=iut(),y=iut(),print(dis[x][y]),putchar(32);
	return 0;
} 

JZOJ 1404 菱形内的计数

题目

一个边长为 n n n的大菱形被均匀地划分成了 n ∗ n n*n nn个边长为1的小菱形组成的网格,但是网格中部分边被抹去了,小L想知道,大菱形内有多少个平行四边形,这些平行四边形内不存在边。


分析

分类讨论,码量少,思维难度大


代码

#include <cstdio>
#include <cctype>
#include <queue>
#define rr register
using namespace std;
char str[1801][1801]; int n,s[1801][1801],ans;
inline signed iut(){
	rr int ans=0; rr char c=getchar();
	while (!isdigit(c)) c=getchar();
	while (isdigit(c)) ans=(ans<<3)+(ans<<1)+(c^48),c=getchar();
	return ans;
}
inline signed exist(int dep,int l,int r){return s[dep][r-1]-s[dep][l];}
inline void dfs(int dep,int l,int r){
	rr int lim=dep;
	while (lim<2*n&&l>1&&r<2*n&&str[lim+1][l-1]==47&&str[lim+1][r+1]==92&&!exist(lim+1,l-1,r+1)) ++lim,--l,++r;
	if (lim<2*n&&l>1&&str[lim+1][l-1]==47&&str[lim+1][r]==47&&!exist(lim+1,l-1,r)){
		++lim,--l;
		while (lim<2*n&&l>1&&str[lim+1][l-1]==47&&str[lim+1][r-1]==47&&!exist(lim+1,l-1,r-1)) ++lim,--l,--r;
	    --r;
	}else if (lim<2*n&&r<2*n&&str[lim+1][l]==92&&str[lim+1][r+1]==92&&!exist(lim+1,l,r+1)){
		++lim,++r;
		while (lim<2*n&&r<2*n&&str[lim+1][l+1]==92&&str[lim+1][r+1]==92&&!exist(lim+1,l+1,r+1)) ++lim,++l,++r; 
	    ++l;
	}
	if (lim<2*n&&str[lim+1][l]==92&&str[lim+1][r]==47&&!exist(lim+1,l,r)){
		++lim;
		while (lim<2*n&&str[lim+1][l+1]==92&&str[lim+1][r-1]==47&&!exist(lim+1,l+1,r-1)) ++lim,++l,--r;
		if (r-l==1) ++ans;
	}
}
signed main(){
	n=iut()<<1;
	for (rr int i=1;i<=n;++i) gets(str[i]+1);
	for (rr int i=1;i<=n;++i)
	for (rr int j=1;j<=n;++j) s[i][j]=s[i][j-1]+(str[i][j]==47||str[i][j]==92);
	for (rr int i=1;i<=n;++i)
	for (rr int j=1;j<n;++j)
	if (str[i][j]==47&&str[i][j+1]==92) dfs(i,j,j+1); 
    return !printf("%d",ans);
}

JZOJ 1405 电缆建设

题目

河流两旁各有 n , m n,m nm个村庄,每个村庄可以用二维坐标表示,其中河流一旁的村庄横坐标均为 x 1 x1 x1,河流另一旁的村庄横坐标均为 x 2 x2 x2。由于地势十分开阔,任意两个村庄可以沿坐标系直线修建一条电缆连接,长度即为两村庄的距离。要修建若干条电缆,使得任意两个村庄都可以通过若干个有电缆连接的村庄相连。


分析

考虑河流一旁的村庄相邻连,对于每个村庄连接直线距离最短的两个村庄,用双指针实现,共有约 4 n 4n 4n条边,跑kruskal即可,时间复杂度 O ( n l o g n ) O(nlogn) O(nlogn)


代码

#include <cstdio>
#include <cctype>
#include <cmath>
#include <algorithm>
#define rr register
using namespace std;
const int N=600101;
struct node{int x,y; long long w;}e[N<<2];
int n,m,xx,a[N],b[N],f[N<<1],tot,now;
inline signed iut(){
	rr int ans=0; rr char c=getchar();
	while (!isdigit(c)) c=getchar();
	while (isdigit(c)) ans=(ans<<3)+(ans<<1)+(c^48),c=getchar();
	return ans; 
}
inline void add(int x,int y,long long w){e[++tot]=(node){x,y,w};}
inline signed getf(int u){return f[u]==u?u:f[u]=getf(f[u]);}
inline long long w(int x){return 1ll*x*x;}
inline bool cmp(node a,node b){return a.w<b.w;}
signed main(){
	n=iut(),m=iut(),xx=w(iut()-iut());
	for (rr int i=1;i<=n;++i) a[i]=a[i-1]+iut(),f[i]=i;
	for (rr int i=1;i<=m;++i) b[i]=b[i-1]+iut(),f[i+n]=i+n;
	for (rr int i=1;i<n;++i) add(i,i+1,w(a[i+1]-a[i]));
	for (rr int i=1;i<m;++i) add(i+n,i+1+n,w(b[i+1]-b[i]));
	for (rr int i=1,j=1;i<=n;++i){
		for (;j<=m&&b[j]<a[i];++j);
		add(i,j+n,w(a[i]-b[j])+xx);
		if (j>1) add(i,j+n-1,w(a[i]-b[j-1])+xx);
	}
	sort(e+1,e+1+tot,cmp); rr double ans=0;
	for (rr int i=1;i<=tot;++i){
		rr int fa=getf(e[i].x),fb=getf(e[i].y);
		if (fa!=fb){
			rr int fc=fa<fb?fa:fb;
			f[fc]=fa^fb^fc,ans+=sqrt(e[i].w);
			if (++now==n+m-1) return !printf("%.2lf",ans);
		}
	}
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值