2022“杭电杯”中国大学生算法设计超级联赛(3)

1011 Taxi

这个题让本来就不擅长偏序问题的我雪上加霜...
问题和曼哈顿距离有关,如果不太擅长的话,可以转换成切比雪夫距离来将x,y分开考虑。
曼哈顿距离\((x_i,y_i) (x_j,y_j) d=|x_i-x_j|+|y_i-y_j|\)
切比雪夫距离\((x_i,y_i) (x_j,y_j) d=max(|x_i-x_j|,|y_i-y_j|)\)
转换关系:曼哈顿距离下的坐标\((x_y)\)->切比雪夫距离下的坐标\((x+y,x-y)\)
对于一个询问\((x',y')\),我们实际上求的是,\(max(min(|x_i-x'|+|y_i-y'|,w_i))(1<=i<=n)\)
转换成切比雪夫距离就是\(max(min(max(|x_i-x'|,|y_i-y'|),w_i))\)
我们交换下顺序,将\(w_i\)放进去\(max(min(|x_i-x'|,w_i),min(|y_i-y'|,w_i))\)
这个时候,一个点的\(x,y\)就不捆绑在一起了,我们可以把x,y单独进行计算,最后取个max即可。只考虑x而言,\(min(|x_i-x'|,w_i)\)意味着,\(x'\)如果在\([x_i-w_i,x_i+w_i]\)之间答案就是\(|x_i-x'|\),超出这个范围答案就是\(w_i\)
所以我们把所有的询问离线下来,将x和y分别找答案,最后取max.
再将n个点,拆成左端点,右端点。从小到大排序,并用优先队列取模拟这个过程即可。

点击查看代码
#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int N=2e5+10;
int T,n,Q;
ll ans[N];
bool tag[N];
struct node{ll x,y,w;}t[N];
struct ask{ll x,y;int id;}q[N];
struct wy{ll pos;int id;}c[N];
struct maxnode
{
	ll val;
	int id;
	bool friend operator <(maxnode a,maxnode b) {return a.val<b.val;}
};
priority_queue<maxnode>qw,qmax,qmin;
int main()
{
//	freopen("1.in","r",stdin);
	scanf("%d",&T);
	while(T--)
	{
		scanf("%d%d",&n,&Q);
		for(int i=1;i<=n;++i)
		{
			ll x,y,w;
			scanf("%lld%lld%lld",&x,&y,&w);
			t[i]=node({x+y,x-y,w});
		}
		for(int i=1;i<=Q;++i) 	
		{
			ll x,y;
			scanf("%lld%lld",&x,&y);
			q[i]=ask({x+y,x-y,i});
		}
		int m=0;
		for(int i=1;i<=n;++i)
		{
			c[++m]={t[i].x-t[i].w,i};
			c[++m]={t[i].x+t[i].w,-i};
		}
		sort(c+1,c+m+1,[&](wy a,wy b){return a.pos<b.pos;});
		sort(q+1,q+Q+1,[&](ask a,ask b){return a.x<b.x;});
		for(int i=1;i<=n;++i) ans[i]=0;
		int now=0;
		while(qw.size()) qw.pop();
		while(qmax.size()) qmax.pop();
		while(qmin.size()) qmin.pop();
		for(int i=1;i<=n;++i)
		{
			tag[i]=false;
			qw.push({t[i].w,i});
		}
		for(int i=1;i<=Q;++i)
		{
			while(now+1<=m&&c[now+1].pos<=q[i].x)
			{
				now++;
				if(c[now].id>0)
				{
					qmax.push({t[c[now].id].x,c[now].id});
					qmin.push({-t[c[now].id].x,c[now].id});
					tag[c[now].id]=true;//进入区间了. 
				}
				else
				{
					int ID=-c[now].id;
					qw.push({t[ID].w,ID});
					tag[ID]=false;//出来区间了. 
				}
			}
			while(qw.size()&&tag[qw.top().id]) qw.pop();
			while(qmax.size()&&!tag[qmax.top().id]) qmax.pop();
			while(qmin.size()&&!tag[qmin.top().id]) qmin.pop();
			if(qw.size()) ans[q[i].id]=max(ans[q[i].id],qw.top().val);
			if(qmax.size()) ans[q[i].id]=max(ans[q[i].id],abs(q[i].x-qmax.top().val));
			if(qmin.size()) ans[q[i].id]=max(ans[q[i].id],abs(q[i].x+qmin.top().val));
		}
		m=0;now=0;
		for(int i=1;i<=n;++i)
		{
			c[++m]={t[i].y-t[i].w,i};
			c[++m]={t[i].y+t[i].w,-i};
		}
		sort(c+1,c+m+1,[&](wy a,wy b){return a.pos<b.pos;});
		sort(q+1,q+Q+1,[&](ask a,ask b){return a.y<b.y;});
		while(qw.size()) qw.pop();
		while(qmax.size()) qmax.pop();
		while(qmin.size()) qmin.pop();
		for(int i=1;i<=n;++i)
		{
			tag[i]=false;
			qw.push({t[i].w,i});
		}
		for(int i=1;i<=Q;++i)
		{
			while(now+1<=m&&c[now+1].pos<=q[i].y)
			{
				now++;
				if(c[now].id>0)
				{
					qmax.push({t[c[now].id].y,c[now].id});
					qmin.push({-t[c[now].id].y,c[now].id});
					tag[c[now].id]=true;//进入区间了. 
				}
				else
				{
					int ID=-c[now].id;
					qw.push({t[ID].w,ID});
					tag[ID]=false;//出来区间了. 
				}
			}
			while(qw.size()&&tag[qw.top().id]) qw.pop();
			while(qmax.size()&&!tag[qmax.top().id]) qmax.pop();
			while(qmin.size()&&!tag[qmin.top().id]) qmin.pop();
			if(qw.size()) ans[q[i].id]=max(ans[q[i].id],qw.top().val);
			if(qmax.size()) ans[q[i].id]=max(ans[q[i].id],abs(q[i].y-qmax.top().val));
			if(qmin.size()) ans[q[i].id]=max(ans[q[i].id],abs(q[i].y+qmin.top().val));
		}
		for(int i=1;i<=Q;++i) printf("%lld\n",ans[i]);
	}
	return 0;
} 

Laser Alarm

计算几何题。不管怎么说,计算几何题永远比同难度下的其他题过的要少。
题意是在三维空间下,给定一些线段,让你找一个平面,使得和线段相交的足够多。
三维不太想的通的话,我们可以现在二维上想,(通过神奇的思考后),我们发现最优答案,经过调整一定会经过某两个线段的端点。换句话说,我们通过枚举任意两个线段的端点组成的直线,一定能吧相交线段的最大值搞出来。那么扩展到三维也是同样的的道理。我们枚举某三个线段的三个点,来确定一个平面,最后计算这个平面与线段的交点,最后取max即可。
具体的判断平面和线段是否相交,我们可以搞出来平面的法向量\(h\),然后取平面上任一点为起点,线段的两个端点作为终点,做出向量\(v_1,v_2\),那么平面和线段相交的充要条件就是\((h·v_1)*(h·v_2)<=0\)即可。其实就是保证\(v_1,v_2\)两个向量一个和\(h\)相同,一个和\(h\)相反。

点击查看代码
#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int N=110;
int T,n;
struct Point
{
	ll x,y,z;
	Point(ll _x=0,ll _y=0,ll _z=0):x(_x),y(_y),z(_z){}
}p[N];
typedef Point Vector;
struct segment{ll sx,sy,sz,ex,ey,ez;}s[N];
Vector operator -(Point a,Point b){return Vector(a.x-b.x,a.y-b.y,a.z-b.z);} 
ll Dot(Vector a,Vector b){return a.x*b.x+a.y*b.y+a.z*b.z;}
Vector Cross(Vector a,Vector b)//三维. 
{
	Vector c;
	c.x=a.y*b.z-a.z*b.y;
    c.y=a.z*b.x-a.x*b.z;
    c.z=a.x*b.y-a.y*b.x;
    return c;
}
inline bool check(Vector h,int i,int j)
{
	Point u1={s[j].sx,s[j].sy,s[j].sz},u2={s[j].ex,s[j].ey,s[j].ez};
	Vector v1=u1-p[i],v2=u2-p[i];
	return (Dot(h,v1)*Dot(h,v2)<=0);
}
int main()
{
//	freopen("1.in","r",stdin);
	scanf("%d",&T);
	while(T--)
	{
		scanf("%d",&n);
		int m=0;
		for(int i=1;i<=n;++i)
		{
			ll x1,y1,z1,x2,y2,z2;
			scanf("%lld%lld%lld%lld%lld%lld",&x1,&y1,&z1,&x2,&y2,&z2);
			s[i]=segment({x1,y1,z1,x2,y2,z2});
			p[++m]=Point(x1,y1,z1);
			p[++m]=Point(x2,y2,z2);
		}
		int ans=-1;
		for(int i=1;i<=m;++i)
			for(int j=i+1;j<=m;++j)
				for(int k=j+1;k<=m;++k)
				{
					Vector v1=p[j]-p[i],v2=p[k]-p[i];
					Vector h=Cross(v1,v2);
					if(h.x==0&&h.y==0&&h.z==0) continue;//三点共线
					int res=0; 
					for(int l=1;l<=n;++l) res+=check(h,i,l);
					ans=max(ans,res);
				}
		if(ans==-1) printf("%d\n",n);
		else printf("%d\n",ans);		
	}
	return 0;	
}
posted @ 2022-07-29 16:36  逆天峰  阅读(41)  评论(0编辑  收藏  举报
作者:逆天峰
出处:https://www.cnblogs.com/gcfer//