2021.05.29【NOIP提高B组】模拟 总结

T1

题意:给你一个图,可以不花代价经过 \(K\) 条边,问从起点到终点的最短路

考试的想法:设 \(dis_{i,j}\) 表示从起点免费了 \(j\) 条边到 \(i\) 的最短路

然后直接跑 \(\text{spfa}\)

结果:\(WA\)

正解:分层图,需要考虑去到下一层就不能回来的情况

分为 \(K\) 层,同一层的 \(u,v\) 边权不变,双向边,去下一层的单向边,权值为 0

然后 \(\text{spfa}\) 超时,用 \(\text{dijkstra}\) 即可

答案:免费任意次的最小值

#include<bits/stdc++.h>
using namespace std;
const int N=10005,M=50005;
int n,m,K,S,T,vis[N*12],ans=2100000000;
int lst[N*12],nxt[M<<6],to[M<<6],qz[M<<6],dis[N*12],cnt;
inline void Ae(int fr,int go,int vl) {
	to[++cnt]=go,qz[cnt]=vl,nxt[cnt]=lst[fr],lst[fr]=cnt;
}
struct node {
	int v,id;
	node(int x,int y):v(x),id(y) {}
	bool operator<(node x) const
	{ return v>x.v; }
};
priority_queue<node> q;
inline void dijkstra() {
	memset(dis,100,sizeof(dis));
	dis[S]=0;
	q.push(node(0,S));
	register int u;
	while(!q.empty()) {
		u=q.top().id,q.pop();
		if(vis[u])continue; vis[u]=1;
		for(int i=lst[u],v;i;i=nxt[i])
			if(dis[v=to[i]]>dis[u]+qz[i]) {
				dis[v]=dis[u]+qz[i];
				q.push(node(dis[v],v));
			}
	}
}
int main() {
	scanf("%d%d%d%d%d",&n,&m,&K,&S,&T);
	++S,++T;
	for(int i=1,u,v,w;i<=m;i++) {
		scanf("%d%d%d",&u,&v,&w);
		++u,++v,Ae(u,v,w),Ae(v,u,w);
		for(int j=1;j<=K;j++) {
			Ae(u+j*n,v+j*n,w);
			Ae(v+j*n,u+j*n,w);
			Ae(u+j*n-n,v+j*n,0);
			Ae(v+j*n-n,u+j*n,0);
		}
	}
	dijkstra();
	for(int i=0;i<=K;i++)
		ans=min(ans,dis[T+n*i]);
	printf("%d",ans);
}

T2

题目大意:给你许多条平行于 \(x\) 轴或 \(y\) 轴的线段

问你最大的十字架大小,大小为 \(R\) 定义为从一个点按上下左右伸出 \(R\) 都有线段覆盖

没有十字架输出 Human intelligence is really terrible

考试时:把所有线段合起来再暴力匹配

结果:看错题了:不会有两条共线线段有交点

正解:可以二分大小 \(R\) ,若把所有线段左右(或上下)都减去 \(R\) 还有线段相交则 \(R\) 成立

所以判断是否有线段相交,用扫描线

T3

题意:找出图中平均值最小的环,无环输出 PaPaFish is laying egg!

考试的想法:二分答案,然后用 \(\text{spfa}\) 一波复杂的操作

正解:把所有边权减去 \(mid\) 若有负环说明 \(mid\) 可以

#include<bits/stdc++.h>
using namespace std;
typedef double db;
const db eps=1e-3;
const int N=1005,M=10005;
int n,m,cnt,lst[N],nxt[M],to[M],vis[N];
db l,r,mid,ans=-1,qz[M],d[N],flg;
inline void Ae(int fr,int go,int vl) {
	to[++cnt]=go,qz[cnt]=1.0*vl;
	nxt[cnt]=lst[fr],lst[fr]=cnt;
}
void spfa(int u) {
	vis[u]=1;
	for(int i=lst[u],v;i;i=nxt[i])
		if(d[u]+qz[i]-mid<d[v=to[i]]) {
			d[v]=d[u]+qz[i]-mid;
			if(vis[v])flg=1;
			else spfa(v);
			if(flg)return;
		}
	vis[u]=0;
}
inline bool chk() {
	memset(d,0,sizeof(d));
	memset(vis,0,sizeof(vis));
	flg=0;
	for(int i=1;i<=n;i++)
		if(!d[i]) {
			spfa(i);
			if(flg)return 1;
		}
	return 0;
}
int main() {
	scanf("%d%d",&n,&m);
	for(int i=1,u,v,w;i<=m;i++) {
		scanf("%d%d%d",&u,&v,&w);
		Ae(u,v,w);
	}
	r=10000000;
	while(l+0.000001<r) {
		mid=(l+r)/2.0;
		if(chk())ans=r=mid;
		else l=mid;
	}
	if(ans==-1)puts("PaPaFish is laying egg!");
	else printf("%.2lf",floor(ans*1000)/1000);
}

T4

题目大意:给你一个排列,求字典序比他大的第一个与他逆序对数一样的排列

考试:直接树状数组+\(\text{next_permutation}\),光荣 30

正解:观察样例可以发现有一段是不变的,考虑找到最大的这个下标

\(Nx(i)\)\(a_i,a_{i+1},a_{i+2},\cdots,a_{n}\) 的逆序对个数

则这个点要满足

  1. 后面有个数比他大
  2. 这个数对后面的数的逆序对小于 \(Nx(i)\)

找到后开始构造,设总共有 \(all\) 个逆序对

发现这个点的数是后面中他的后继,于是 \(ans_{p}\) 填好了

然后现在我们已经有(前面一部分逆序对数)+(\(ans_p\)对后面一部分的逆序对数)个逆序对,记为 \(sum\)

填剩下的位置,发现若 (填 \(i\) 产生的逆序对数)+(原有的 \(sum\))+(剩下最大逆序对数)\(\ge all\),则合法

好像可以二分,于是加一个权值线段树查找第 \(k\) 大即可。每次查询要更新 \(sum\)

#include<bits/stdc++.h>
#define G getchar
#define P putchar
using namespace std;
inline int rd() {
	register int x=0,f=1;
	char C=getchar();
	for(;C<'0'||C>'9';C=G())f&=C^45;
	for(;C>'/'&&C<':';C=G())x=(x<<1)+(x<<3)+(C^48);
	return f?x:-x;
}
void put(int x) { if(x>9)put(x/10); P(x%10|48); }
typedef long long LL;
const int N=500005;
int n,x[N],pos,mx,y[N],l,r,mid,out;
LL t[N],all,nx[N],val[N<<2],sum;
inline LL MX(LL x) { return x*(x-1)/2; }
inline void add(int p,int v) { for(;p<=n;p+=p&-p)t[p]+=1LL*v; }
inline LL ask(LL p) { register LL s=0; for(;p;p-=p&-p)s+=t[p]; return s; }
#define ls rt<<1
#define rs rt<<1|1
void mdy(int p,int v,int l,int r,int rt) {
	if(l==r) { val[rt]+=v; return; }
	register int mid=l+r>>1;
	if(p<=mid)mdy(p,v,l,mid,ls);
	else mdy(p,v,mid+1,r,rs);
	val[rt]=val[ls]+val[rs];
} 
int fnd(int k,int l,int r,int rt) {
	if(l==r)return l;
	register int mid=l+r>>1;
	if(k<=val[ls])return fnd(k,l,mid,ls);
	else return fnd(k-val[ls],mid+1,r,rs);
}
#undef ls
#undef rs
int main() {
	n=rd();
	for(int i=1;i<=n;i++)x[i]=rd();
	for(int i=n;i;i--) {
		add(x[i],1),nx[i]=ask(x[i]-1);
		all+=nx[i];
	}
	mx=x[n],sum=nx[n];
	for(int i=n-1;i;i--) {
		mx=max(mx,x[i]),sum+=nx[i];
		if(x[i]^mx && nx[i]<sum) {
			pos=i; break;
		}
	}
	sum=0;
	for(int i=1;i<pos;i++)put(x[i]),P(32),sum+=nx[i];
	out=INT_MAX;
	for(int i=pos+1;i<=n;i++)
		if(x[i]>x[pos])out=min(out,x[i]);
	put(out),P(32);
	for(int i=pos;i<=n;i++)
		if(x[i]^out)mdy(x[i],1,1,n,1),sum+=(x[i]<out);
	for(int i=pos+1;i<=n;i++) {
		l=1,r=n-i+1;
		while(l<r) {
			mid=l+r>>1;
			if(mid-1+sum+MX(n-i)>=all)
				r=mid;
			else l=mid+1;
		}
		out=fnd(l,1,n,1),put(out),P(32);
		sum+=l-1,mdy(out,-1,1,n,1);
	}
}

总结

这些题我该怎么想:

T1:遇到图论不能单纯设状态,最好连边

T2:认真看题+二分的运用+扫描线的题型

T3:遇到环上值的题多想想负环,同时了解图的边权是可以变的

T4:贪心策略一直不强,逆序对的构造要了解,同时多观察样例

总结:最近的题知识点普遍不少,需要好好消化

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
资源包主要包含以下内容: ASP项目源码:每个资源包中都包含完整的ASP项目源码,这些源码采用了经典的ASP技术开发,结构清晰、注释详细,帮助用户轻松理解整个项目的逻辑和实现方式。通过这些源码,用户可以学习到ASP的基本语法、服务器端脚本编写方法、数据库操作、用户权限管理等关键技术。 数据库设计文件:为了方便用户更好地理解系统的后台逻辑,每个项目中都附带了完整的数据库设计文件。这些文件通常包括数据库结构图、数据表设计文档,以及示例数据SQL脚本。用户可以通过这些文件快速搭建项目所需的数据库环境,并了解各个数据表之间的关系和作用。 详细的开发文档:每个资源包都附有详细的开发文档,文档内容包括项目背景介绍、功能模块说明、系统流程图、用户界面设计以及关键代码解析等。这些文档为用户提供了深入的学习材料,使得即便是从零开始的开发者也能逐步掌握项目开发的全过程。 项目演示与使用指南:为帮助用户更好地理解和使用这些ASP项目,每个资源包中都包含项目的演示文件和使用指南。演示文件通常以视频或图文形式展示项目的主要功能和操作流程,使用指南则详细说明了如何配置开发环境、部署项目以及常见问题的解决方法。 毕业设计参考:对于正在准备毕业设计的学生来说,这些资源包是绝佳的参考材料。每个项目不仅功能完善、结构清晰,还符合常见的毕业设计要求和标准。通过这些项目,学生可以学习到如何从零开始构建一个完整的Web系统,并积累丰富的项目经验。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值