7.17海亮集训NOIP模拟赛题解

A

有幸找到原题洛谷P4053

一般长得像dp的,但数据范围又很大,dp会TLE,MLE的题就可以考虑反悔贪心了
当然是因为我用dp没做出来然后放弃了

先把结尾从小到大排序,因为容量必须是要一直递增的

维护多余的时间p,若当前所需要的时间小于p,找到当前已经做了的任务中的最大值
如果把老任务换成当前的新任务能够节约时间,即新任务的时间小于当前已经做了的任务中的最大值
那么用新任务代替老任务,即p=p+q.top()-a[i].a;

怎么维护最大值?优先队列。

#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
const int maxn=1.5e5+5;
void read(LL& x){
	char c;
	bool f=0;
	while((c=getchar())<48) f|=(c==45);
	x=c-48;
	while((c=getchar())>47) x=x*10+c-48;
	x=(f ? -x : x);
	return;
}
struct task{
	LL a,b;
};
bool cmp(task a,task b){
	return a.b<b.b;
}
task a[maxn];
priority_queue<LL> q;
LL n;
int main(){
	//freopen("sky.in","r",stdin);
	//freopen("sky.out","w",stdout);
	read(n);
	for(int i=1;i<=n;i++){
		read(a[i].a),read(a[i].b);
	}
	sort(a+1,a+n+1,cmp);
	int ans=0;
	LL p=0;
	for(int i=1;i<=n;i++){
		p+=a[i].b-a[i-1].b;
		if((!q.empty())&&p<a[i].a&&a[i].a<q.top()){
			p=p+q.top()-a[i].a;
			q.pop();
			q.push(a[i].a);
		}
		else if(a[i].a<=p){
			p-=a[i].a,++ans;
			q.push(a[i].a);
		}
	}
	printf("%d",ans);
	return 0;
}

B

把题先放在这里吧
数学题暂时没看懂
image

官解

image

C

image
如果对于一个网格点,能够快速找到包含这个点的所有矩形的矩形交,那就可以直接遍历每个点求解了

网格图并不大,可以用二维数组,用差分维护每个矩形上下左右四条边,并记录每个点向上,向下,向左,向右所遇到的第一条边就可以了

矩形交的面积计算如下(r[i][j]-l[i][j]+1)*(d[i][j]-u[i][j]+1)

#include<bits/stdc++.h>
using namespace std;
const int maxn=2005;
void read(int& x){
	char c;
	bool f=0;
	while((c=getchar())<48) f|=(c==45);
	x=c-48;
	while((c=getchar())>47) x=x*10+c-48;
	x=(f ? -x : x);
	return;
}
struct node{
	int u,d,l,r;
};
node a[maxn][maxn];
int u[maxn][maxn],d[maxn][maxn],l[maxn][maxn],r[maxn][maxn];
int n,m,k;
int main(){
	//freopen("rec.in","r",stdin);
	//freopen("rec.out","w",stdout);
	read(n),read(m),read(k);
	int x1,y1,x2,y2;
	for(int i=1;i<=k;i++){
		read(x1),read(y1),read(x2),read(y2);
		++a[x1][y1].u,--a[x1][y2+1].u;
		++a[x2][y1].d,--a[x2][y2+1].d;
		++a[x1][y1].l,--a[x2+1][y1].l;
		++a[x1][y2].r,--a[x2+1][y2].r;
	}
	for(int i=1;i<=n;i++){
		for(int j=1;j<=m;j++){
			a[i][j].u=a[i][j-1].u+a[i][j].u;
			a[i][j].d=a[i][j-1].d+a[i][j].d;
			a[i][j].l=a[i-1][j].l+a[i][j].l;
			a[i][j].r=a[i-1][j].r+a[i][j].r;
		}
	}
	memset(u,-1,sizeof(u));
	memset(d,-1,sizeof(d));
	memset(l,-1,sizeof(l));
	memset(r,-1,sizeof(r));
	for(int i=1;i<=n;i++){
		for(int j=1;j<=m;j++){
			u[i][j]=(a[i][j].u ? i : u[i-1][j]);
		}
	}
	for(int i=n;i>=1;i--){
		for(int j=1;j<=m;j++){
			d[i][j]=(a[i][j].d ? i : d[i+1][j]);
		}
	}
	for(int i=1;i<=n;i++){
		for(int j=1;j<=m;j++){
			l[i][j]=(a[i][j].l ? j : l[i][j-1]);
		}
	}
	for(int i=1;i<=n;i++){
		for(int j=m;j>=1;j--){
			r[i][j]=(a[i][j].r ? j : r[i][j+1]);
		}
	}
	int ans=1e9+1;
	for(int i=1;i<=n;i++){
		for(int j=1;j<=m;j++){
			if(l[i][j]<0||r[i][j]<0||u[i][j]<0||d[i][j]<0) continue;
			ans=min(ans,(r[i][j]-l[i][j]+1)*(d[i][j]-u[i][j]+1));
		}
	}
	printf("%lld",ans);
	return 0;
}

被巨佬hack掉了qwq,又要重新订正了

重新订正

如下数据会hack该程序:

21 21 5
9 1 9 11
1 12 10 12
10 10 21 10
11 11 11 21
1 1 21 21

这幅图大概长这样:
image
(注:每个点表示覆盖这个点的矩形个数)

原因是四个矩形的四条边把程序迷惑住了,误判了中心的伪矩形

考虑哈希,把每个由同一矩形覆盖的点都异或上一个相同的随机数,由此会得到许多块

用dfs求连通块,然后判断该连通块的上下左右四条边是否包含在原来矩形的边中

我觉得原题解描述的更加清楚:
image

具体见代码注释

#include<bits/stdc++.h>
using namespace std;
const int maxn=2005;
mt19937 rng(time(0));
void read(int& x){
	char c;
	bool f=0;
	while((c=getchar())<48) f|=(c==45);
	x=c-48;
	while((c=getchar())>47) x=x*10+c-48;
	x=(f ? -x : x);
	return;
}
int p[maxn][maxn];
int u[maxn][maxn],d[maxn][maxn],l[maxn][maxn],r[maxn][maxn];
bool vis[maxn][maxn];
int wx[4]={0,0,-1,1},wy[4]={-1,1,0,0};
int n,m,k;
void dfs(int x,int y,int& mx,int& my,int& cnt){
	++cnt;
	vis[x][y]=1;
	mx=max(mx,x),my=max(my,y);
	for(int i=0;i<4;i++){
		int xi=x+wx[i],yi=y+wy[i];
		if(xi>=1&&xi<=n&&yi>=1&&yi<=n&&p[x][y]==p[xi][yi]&&!vis[xi][yi]){
			dfs(xi,yi,mx,my,cnt);
		}
	}
}
int main(){
	//freopen("rec.in","r",stdin);
	//freopen("rec.out","w",stdout);
	read(n),read(m),read(k);
	int x1,y1,x2,y2;
	for(int i=1;i<=k;i++){
		read(x1),read(y1),read(x2),read(y2);
		++u[x1][y1],--u[x1][y2+1];
		++d[x2][y1],--d[x2][y2+1];
		++l[x1][y1],--l[x2+1][y1];
		++r[x1][y2],--r[x2+1][y2];
		int j=rng();
		p[x1][y1]^=j,p[x1][y2+1]^=j,p[x2+1][y1]^=j,p[x2+1][y2+1]^=j;
	}
	for(int i=1;i<=n;i++){
		for(int j=1;j<=m;j++){
			u[i][j]+=u[i][j-1],d[i][j]+=d[i][j-1];
			l[i][j]+=l[i-1][j],r[i][j]+=r[i-1][j];
			p[i][j]=p[i][j]^p[i-1][j]^p[i][j-1]^p[i-1][j-1];
		}
	}
	for(int i=1;i<=n;i++){
		for(int j=1;j<=m;j++){
			u[i][j]=(u[i][j] ? 1 : 0);
			d[i][j]=(d[i][j] ? 1 : 0);
			l[i][j]=(l[i][j] ? 1 : 0);
			r[i][j]=(r[i][j] ? 1 : 0);
		}
	}
	for(int i=1;i<=n;i++){
		for(int j=1;j<=m;j++){
			u[i][j]=u[i][j-1]+u[i][j];
			d[i][j]=d[i][j-1]+d[i][j];
			l[i][j]=l[i-1][j]+l[i][j];
			r[i][j]=r[i-1][j]+r[i][j];
		}
	}
	int ans=1e9+1,ansi;
	for(int i=1;i<=n;i++){
		for(int j=1;j<=m;j++){
			if(!vis[i][j]){
				x1=i,y1=j,x2=-1,y2=-1,ansi=0;
				dfs(x1,y1,x2,y2,ansi);
				if(u[x1][y2]-u[x1][y1-1]==y2-y1+1&&\  
				   d[x2][y2]-d[x2][y1-1]==y2-y1+1&&\
				   l[x2][y1]-l[x1-1][y1]==x2-x1+1&&\
				   r[x2][y2]-r[x1-1][y2]==x2-x1+1&&\   //由于u,d,l,r已经被更新为1,0的前缀和,若区间内1的数量相等,则说明该边是有在原边中的
				   ansi==(x2-x1+1)*(y2-y1+1)){  //防止连通块是畸形的情况,x2,y2表示的是连通块中最大的x,y值,若该式子满足,一定能说明这是矩形
					ans=min(ans,ansi);
				}
			}
		}
	}
	printf("%d",ans);
	return 0;
}

E

努力补题中

posted @ 2025-07-17 18:37  huangems  阅读(16)  评论(0)    收藏  举报