CF1028
橙绿黄蓝蓝紫黑紫 先只做前4题吧……
我太菜了 不——开——心——
A. Find Square
题目大意:
给出一个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
题目大意:
令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
题目大意:
在平面直角坐标系中给出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
题目大意:
维护两个集合,使得\(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;
}
后面不写了。