题意
给定一个棋盘若干棋子,问最多可以找出多少棋子符合下列条件,在一条对角线上且它们中的任意两个沿对角线上路径的格数不小于
p2i+p2j+C
思路
首先很轻易地能推出这个式子的传递性,所以我们只需要对一条对角线从上往下放棋子,不断地看对于要放的这个棋子,最后一个可以跟它符合条件的棋子是哪个,会形成一条多长的链。将式子简单移项得
j−p2j≥p2i+i+C−1
,所以每次找到符合条件的最后一项,再找到前缀里最大的值,加1并更新最大值,用树状数组维护
代码
#include <cstdio>
#include <vector>
#include <algorithm>
using namespace std;
long long T,n,m,C;
typedef struct bishop
{
long long r;
long long c;
long long p;
long long id;
}bishop;
bool cmp1(bishop a,bishop b)
{
return a.r<b.r;
}
bool cmp2(bishop a,bishop b)
{
if(a.p*a.p+a.r+C-1!=b.p*b.p+b.r+C-1)
return a.p*a.p+a.r+C-1<b.p*b.p+b.r+C-1;
else return a.r<b.r;
}
vector<bishop> v[400001];
long long hsh[100001],BIT[100001];
long long e;
long long lowbit(long long x)
{
return x&(-x);
}
void change(long long x,long long d)
{
while(x<=e)
{
BIT[x]=max(BIT[x],d);
x+=lowbit(x);
}
}
long long qmax(long long x)
{
long long ret=0;
while(x>0)
{
ret=max(ret,BIT[x]);
x-=lowbit(x);
}
return ret;
}
long long binarysearch(long long l,long long r,long long x)
{
long long mid=(l+r)/2;
if(l==r)
return l;
if(r-l==1)
{
if(hsh[r]<=x)
return r;
return l;
}
if(hsh[mid]>x)
return binarysearch(l,mid-1,x);
else return binarysearch(mid,r,x);
}
int main()
{
freopen("bishops.in","r",stdin);
bishop temp;
long long p,ans,anss;
scanf("%I64d",&T);
while(T--)
{
ans=0;
scanf("%I64d%I64d%I64d",&n,&m,&C);
for(long long i=0;i<m;i++)
{
scanf("%I64d%I64d%I64d",&temp.r,&temp.c,&temp.p);
v[temp.r+temp.c].push_back(temp);
v[temp.r-temp.c+3*n].push_back(temp);
}
for(long long i=0;i<4*n;i++)
{
e=v[i].size();
sort(v[i].begin(),v[i].end(),cmp2);
for(long long j=0;j<v[i].size();j++)
{
v[i][j].id=j+1;
hsh[j+1]=v[i][j].p*v[i][j].p+v[i][j].r+C-1;
}
sort(v[i].begin(),v[i].end(),cmp1);
for(long long j=0;j<v[i].size();j++)
{
p=binarysearch(0,v[i].size(),v[i][j].r-v[i][j].p*v[i][j].p);
change(v[i][j].id,qmax(p)+1);
}
anss=qmax(v[i].size());
if(anss>ans)
ans=anss;
for(long long j=1;j<=v[i].size();j++)
BIT[j]=0;
}
printf("%I64d\n",ans);
for(long long i=0;i<4*n;i++)
v[i].clear();
}
return 0;
}