P10025题解

P10025 「HCOI-R1」孤独的 sxz

题目传送门

题解

这个真的是 div2 的 T2 吗?

分享一下个人解法,不保证是最简洁的但不需要离散化或权值线段树。注意到这个题不按常理出牌,求的是最大曼哈顿距离,所以如果没有“不能和其他人坐在同一个地方”这个限制那么答案一定是四个角的其中一个。

小小证明一下:分开考虑 \(x,y\),这里以 \(x\) 为例,向上一格的收益是第一维比当前 \(x\) 大的人数减去剩下的,向下同理,假设向上更优,那么继续向上一定依然更优,因为第一维比当前 \(x\) 大的人数单调不减,剩下的人数单调不增,故到达边界一定更优。

那有了限制之后怎么做呢?沿用上面的证明,注意到答案一定在四个角的 \(k\times k\) 等腰直角三角形中。两个维度是独立的,故分开考虑,使用双指针或 map 不难处理出这 \(O(k)\) 行、列的独立贡献。

接下来,以左上角的三角形为例:我们先把 \((1,1)\) 的贡献扔进优先队列,每次取出贡献最大的点,如果不被占用则更新答案并退出,否则把可能成为答案的 \((i,j+1)\)\((i+1,j)\) 扔进优先队列,这也算是堆的经典应用了。

容易发现我们最多只会循环 \(k\) 次,故复杂度 \(O(k\log k)\)

旋转坐标轴,重复 \(4\) 次即可。

代码:

#include<bits/stdc++.h>
using namespace std;
inline int rd() {
	int s=0,m=0;char ch=getchar();
	while(!isdigit(ch)) {if(ch=='-')m=1;ch=getchar();}
	while( isdigit(ch)) s=(s<<3)+(s<<1)+(ch^48),ch=getchar();
	return m?-s:s;
}
int n,m,k;
map<pair<int,int>,int>mp,vis;
int mpx[400005],mpy[400005];
struct QWQ {int x,y;} a[400005],aaa[400005];
long long sx[400005],sy[400005],Ans;
struct qwq {
	int x,y;long long p;
	bool operator<(const qwq& r)const {return p<r.p;}
};
priority_queue<qwq> pq;
void solve() {
	while(pq.size()) pq.pop();
	memset(sx,0,sizeof(sx));
	memset(sy,0,sizeof(sy));
	memset(mpx,0,sizeof(mpx));
	memset(mpy,0,sizeof(mpy));
	mp.clear();vis.clear();
	int ln=min(k+2,n),lm=min(k+2,m);
	for(int i=1;i<=k;i++) {
		if(a[i].x<=ln&&a[i].y<=lm)
			mp[make_pair(a[i].x,a[i].y)]=1;
		if(a[i].x<=ln) mpx[a[i].x]++;
		if(a[i].y<=lm) mpy[a[i].y]++;
		sx[1]+=a[i].x-1,sy[1]+=a[i].y-1;
	}
	for(int i=2,s=mpx[1];i<=ln;i++)
		sx[i]=sx[i-1]+s*2-k,s+=mpx[i];
	for(int i=2,s=mpy[1];i<=lm;i++)
		sy[i]=sy[i-1]+s*2-k,s+=mpy[i];
	pq.push({1,1,sx[1]+sy[1]});
	while(1) {
		qwq f=pq.top();pq.pop();
		if(!mp[make_pair(f.x,f.y)]) {Ans=max(Ans,f.p);return;}
		if(f.x!=n&&!vis[make_pair(f.x+1,f.y)]) vis[make_pair(f.x+1,f.y)]=1,pq.push({f.x+1,f.y,sx[f.x+1]+sy[f.y]});
		if(f.y!=m&&!vis[make_pair(f.x,f.y+1)]) vis[make_pair(f.x+1,f.y)]=1,pq.push({f.x,f.y+1,sx[f.x]+sy[f.y+1]});
	}
}
signed main() {
	cin>>n>>m>>k;
	for(int i=1;i<=k;i++)
		aaa[i].x=rd(),aaa[i].y=rd();
	for(int i=1;i<=k;i++)
		a[i].x=aaa[i].x,a[i].y=aaa[i].y;
	solve();
	for(int i=1;i<=k;i++)
		a[i].x=n-aaa[i].x+1,a[i].y=aaa[i].y;
	solve();
	for(int i=1;i<=k;i++)
		a[i].x=aaa[i].x,a[i].y=m-aaa[i].y+1;
	solve();
	for(int i=1;i<=k;i++)
		a[i].x=n-aaa[i].x+1,a[i].y=m-aaa[i].y+1;
	solve();
	cout<<Ans;
	return 0;
}
posted @ 2024-01-19 15:33  operator-  阅读(24)  评论(0编辑  收藏  举报