2022“杭电杯”中国大学生算法设计超级联赛(1)部分题题解
Ball
这个题如果我们就直接按照题意懂点的角度去思考问题的话,发现很难实现这个操作...
这个时候就要换个角度,可以尝试从边的角度去思考这个题。
如果从边的角度去思考问题的话,这个题就要求找到三个边,使得长度为中间的边的为质数。
既然要求由距离的限制,我们不妨将所有的边从小到大排序,排完序之后考虑从小到大枚举每一条边,这样当我们枚举到i时,比i小的边我们都已经枚举过了。到第i条边时,我们只需要找x连的比i小边,y连的比i大的边。正好我们可以在之前的枚举中求完答案后,我们可以做一个标记。这样就知道哪条边,哪条边大。我们考虑用bitset去存一个点所连的边的关系。为1说明,比当前i小。s[i][j]表示i-j这条边比当前边的权值小。则我们可以直接让s[x]^s[y]即可。两者之间1的个数即为答案。
点击查看代码
#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int N=2010,M=2e6+10,maxn=2e5+10;
int n,m,T,px[N],py[N];
bool vis[maxn+10];
struct wy{int x,y,v;}b[M];
bitset<N>s[N];
inline int dis(int x,int y)
{
return abs(px[x]-px[y])+abs(py[x]-py[y]);
}
int main()
{
// freopen("1.in","r",stdin);
for(int i=2;i<=maxn;++i)
for(int j=2;i*j<=maxn;++j) vis[i*j]=1;
scanf("%d",&T);
while(T--)
{
scanf("%d%d",&n,&m);
for(int i=1;i<=n;++i)
{
scanf("%d%d",&px[i],&py[i]);
s[i].reset();
}
int cnt=0;
for(int i=1;i<=n;++i)
for(int j=i+1;j<=n;++j) b[++cnt]={i,j,dis(i,j)};
sort(b+1,b+cnt+1,[&](wy a,wy b){return a.v<b.v;});
ll ans=0;
for(int i=1;i<=cnt;++i)
{
if(!vis[b[i].v]) ans+=(s[b[i].x]^s[b[i].y]).count();
s[b[i].x][b[i].y]=1;
s[b[i].y][b[i].x]=1;
}
printf("%lld\n",ans);
}
return 0;
}