CF1028

橙绿黄蓝蓝紫黑紫 先只做前4题吧……
我太菜了 不——开——心——

A. Find Square

原题小链接(CF)

题目大意:

给出一个n*m的矩阵,矩阵中有一个由‘B’组成的正方形(边长为奇数),求正方形的中心点在矩阵中的坐标。

解题思路:

只要知道正方形四个顶点的坐标,求中心点就很容易了。所以只需要记录一下正方形行和列的范围,输出两者的中点就ok了

代码
#incIude <bits/stdc++.h>
using namespace std;
const int N=120;
int n,m;
int r1=0x3f3f3f3f,r2,l1=0x3f3f3f3f,l2;

int main()
{
	cin>>n>>m;
	for (int i=1;i<=n;i++)
	{
		for (int j=1;j<=m;j++)
		{
			char c;
			cin>>c;
			if (c=='B')
			{
				r1=min(i,r1);
				r2=max(i,r2);
				l1=min(l1,j);
				l2=max(l2,j);
			}
		}
	}
	cout<<(r1+r2)/2<<" "<<(l1+l2)/2;
	return 0;
}


B. Unnatural Conditions

原题小链接(CF)

题目大意:

令S(a)表示a数位位上的数字之和,给出n,m,输出a,b,使S(a)>=n,S(b)>=n,S(a+b)<=m

解题思路:

因为n,m>=1,我们不妨使得S(a+b)=1,即前面每位的和为9、最后一位和为10,然后使S(a),S(b)>=n便可

代码
#incIude <bits/stdc++.h>
using namespace std;
int n,m;
string a,b;

int main()
{
	cin>>n>>m;
	while (n>4)
	{
		a+=(char)('0'+4);
		b+=(char)('0'+5);
		n-=4;
	}
	a+=(char)('0'+5);
	b+=(char)('0'+5);
	cout<<a<<endl<<b;
	return 0;
}


C. Rectangles

原题小链接(CF)

题目大意:

在平面直角坐标系中给出n个矩形,需找到一点(x,y)使得该点至少被n-1个矩形包含(内部或边界)

解题思路:

一眼想出取所有矩形x、y的交集,但是这个题可能不能取到所有矩形的坐标的交集。于是乎,需要枚举删除第i个矩形。那么每次怎么操作呢……

预处理出最大的x1、y1与最小的x2、y2(取所有矩形的交集),若最值是在此次删除的矩形中取到的,那么这次就要不取最值而是取次大值、次小值(因为最值在的矩形被删了,无法再取……所以只能取次值……)

当目前枚举的x1、x2、y1、y2合法时,即x1<=x2&&y1<=y2时,它就可以作为答案,输出结束即可。

小小声:刚开始把x、y单拎出来算的,就没有考虑到可能x和y删的不是同一个矩形,也就是多删矩形了……然后就挂了

不可爱的代码
#incIude <bits/stdc++.h>
using namespace std;
int inf=2147483647;
int n;
int mxx[4],mnx[4],mxy[4],mny[4];//1最大值,2次大值,3很好理解 

int main()
{
	scanf("%d",&n);
	mxx[1]=mxy[1]=-inf;
	mnx[1]=mny[1]=inf;
	for (int i=1;i<=n;i++)
	{
		int x1,yy1,x2,y2;
		scanf("%d%d%d%d",&x1,&yy1,&x2,&y2);
		if (x1>=mxx[1])//预处理出所有的最值与次大值、次小值
		{
			mxx[2]=mxx[1];
			mxx[1]=x1;
			mxx[3]=i;
		}
		else mxx[2]=max(mxx[2],x1);
		if (x2<=mnx[1])
		{
			mnx[2]=mnx[1];
			mnx[1]=x2;
			mnx[3]=i;
		}
		else mnx[2]=min(mnx[2],x2);
		if (yy1>=mxy[1])
		{
			mxy[2]=mxy[1];
			mxy[1]=yy1;
			mxy[3]=i;
		}
		else mxy[2]=max(mxy[2],yy1);
		if (y2<=mny[1])
		{
			mny[2]=mny[1];
			mny[1]=y2;
			mny[3]=i;
		}
		else mny[2]=min(mny[2],y2);
	}
	for (int i=1;i<=n;i++)//枚举删除的矩形 
	{
		int a,b,c,d;
		if (i==mxx[3]) a=mxx[2];//判断是否是在该矩形取到的最值
		else a=mxx[1];
		if (i==mnx[3]) b=mnx[2];
		else b=mnx[1];
		if (i==mxy[3]) c=mxy[2];
		else c=mxy[1];
		if (i==mny[3]) d=mny[2];
		else d=mny[1];
		if (a<=b&&c<=d)
		{
			printf("%d %d",a,c);
			break;
		}
	}
	return 0;
}


D. Order book

原题小链接(CF)

题目大意:

维护两个集合,使得\(A\)中任意元素都小于\(B\)中元素。后给定\(n(1\leqslant n\leqslant363304)\)个操作:
操作\(ADD\):把\(x\)插入任意一个集合中;
操作\(ACCEPT\):把\(x\)从它所在的集合中删除,要求删除是必须是\(A\)中的最大值或者\(B\)中的最小值。
求插入的方案数。

解题思路:

注意到操作1很不好考虑,所以我们考虑操作2对于操作1的影响。

每次的操作2可以理解为在x的位置将大集合(假设大集合是单调递增的)分为两半,小于x的数一定在\(A\)集合中,大于x的数一定在\(B\)集合中,而x可以在\(A\)集合也可以在\(B\)集合,共有两种方案。当完成此次操作2后,\(A\)中最大值一定是x左边的数,\(B\)中最小值一定是x右边的数,记他们为\(l,r\)。对于以后的操作,小于\(l\)的数一定只能在\(A\)集合中,大于\(r\)的数一定只能在\(B\)集合中,所以只有\(x\in(l,r)\)才能够执行操作2,\((l,r)\)就形成了一个缓冲范围。

于是乎,我们可以用set来维护大集合,操作1往set中插入元素,操作2判断并维护\(l,r\)即可。

还有一个容易遗漏的点。当n次操作以操作1结尾时,因没有新的操作2的限制,所以这些操作的\(x\in(l,r)\)也是分割大集合的一种方案,用cnt记录\(x\in(l,r)\)的个数,最后ans*=(cnt+1)就更新完了。
(为什么是n+1呢?因为n个数可以插n+1个板、就是有n+1种方案啦)

可爱的小代码
#incIude <bits/stdc++.h>
using namespace std;
const int MOD=1e9+7;
const long long inf=4e10;
long long n;
string op;
long long x;
set <long long> s; 
long long l=-inf,r=inf;//A堆最大、B堆最小的缓冲范围 
long long cnt=1,ans=1;

int main()
{
	s.insert(l);s.insert(r);
	cin>>n;
	while (n--)
	{
		cin>>op>>x;
		if (op=="ADD")
		{
			if (x>l&&x<r) cnt++;//可选 
			s.insert(x);
		}
		if (op=="ACCEPT")
		{
			if (x<l||x>r)//无法操作 
			{
				cout<<"0";
				return 0;
			}
			if (l<x&&x<r) ans=(ans*2)%MOD;//计入答案 
			cnt=1;//更新cnt 
			set <long long>::iterator it=s.find(x);//更新边界 
			it--; l=*(it);
			it++; it++; r=*(it);
			it--; s.erase(it);
		}
	}
	cout<<ans*cnt%MOD;
	return 0;
}


后面不写了。

posted @ 2024-10-30 11:47  还是沄沄沄  阅读(253)  评论(6编辑  收藏  举报