链接 小红的点构造
题目大意就是构造n个点,其中k对满足曼哈顿距离为1
思路
我们先看什么时候可以最大构造,设 个点平铺在
的平面上,那么曼哈顿距离为1的点对有
对,考虑其最大值:
但是这里是整数向下取整得 时是有解的,同时也说明了点聚集地越似正方形,点对的贡献值越大。那么我们考虑先贪心地选择正方形,如何构造这样紧密的点集呢?
不难发现有个蛇形路径可以满足我们的需求:
17 18 19 20 21
16 5 6 7 22
15 4 1 8 23
14 3 2 9 24
13 12 11 10 25
28 27 26
这里面的数字代表点的序号,按照此次序摆放的点最紧密。如此摆放的点集每次对贡献增加最多2,最少1,也就是存在总体的贡献 那么如果增加的贡献为2导致大于k,此时直接在整体的最右侧加个点就行了。
如果剩下还有点,那就找个远点的地方别挨着放。
代码
这里说明下如何构造这个蛇形点列,枚举几个环就会发现第 环的点数总是
,那么每个环有四个边,每个边有
个点,如此遍历就很容易构造了。
以及使用set来查找相邻点 实际上用二维数组也可以,但我没试过会不会MLE
const int dx[]={0,0,-1,1};
const int dy[]={-1,1,0,0};
int main()
{
ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
int n,k; cin >> n >> k;
if(k>2*n-2*sqrt(n))return cout << "No",0;
vector<pii> a,ans;
set<pii> st;
int sx=0, sy=0;
a.push_back({sx,sy});
for(int l=1; l<=1e3; l++)
{
a.push_back({sx,--sy});
for(int tx=0; tx<2*l-1; tx++)a.push_back({--sx,sy});
for(int ty=0; ty<2*l; ty++)a.push_back({sx,++sy});
for(int tx=0; tx<2*l; tx++)a.push_back({++sx,sy});
for(int ty=0; ty<2*l; ty++)a.push_back({sx,--sy});
}
int p=0, fx=2e6;
int s=0;
while(ans.size()<n)
{
if(s==k)
{
ans.push_back({fx,fx});
fx+=2;
continue;
}
auto [x,y]=a[p++];
int cnt=0;
for(int i=0; i<4; i++)
{
int nx=x+dx[i], ny=y+dy[i];
if(st.count({nx, ny}))cnt++;
}
if(cnt+s<=k)
{
s+=cnt;
ans.push_back({x,y});
st.insert({x,y});
}
else
{
int mx=INT_MIN, my=0;
for(auto &[xx,yy]:st)if(xx>mx)
mx=xx, my=yy;
s+=1;
st.insert({mx+1,my});
ans.push_back({mx+1,my});
}
}
cout << "Yes\n";
for(auto &[x,y]:ans)cout << x << " " << y << endl;
return 0;
}

京公网安备 11010502036488号