PS:如果读过题了可以跳过题目描述直接到题解部分
暂无提交链接
题目
题目背景
1363有一个无向图
题目描述
1363有一个无向图
G
=
(
V
,
E
)
G=(V,E)
G=(V,E), 其 中
V
=
1
,
2
,
…
,
n
V=1,2,…,n
V=1,2,…,n。 对 于 中 的 任 意 一 个 点
v
v
v, 有 两 个 相 关 的 点 权
f
x
(
v
)
,
f
y
(
v
)
fx(v),fy(v)
fx(v),fy(v)。
(
i
,
j
)
∈
E
(i,j)∈E
(i,j)∈E 当 且 仅 当
∣
f
x
(
i
)
−
f
x
(
j
)
∣
+
∣
f
y
(
i
)
−
f
y
(
j
)
∣
≤
d
|fx(i)-fx(j)|+|fy(i)-fy(j)|≤d
∣fx(i)−fx(j)∣+∣fy(i)−fy(j)∣≤d。 求
G
G
G 的最大团的点数。
最大团定义:
如果
U
⊆
V
U⊆V
U⊆V且
U
≠
V
U≠V
U=V,且对任意两个顶点
u
,
v
∈
U
u,v∈U
u,v∈U有
(
u
,
v
)
∈
E
(u,v)∈E
(u,v)∈E,则称
U
U
U是
G
G
G的完全子图。
G
G
G的完全子图
U
U
U是
G
G
G的团。
G
G
G的最大团是指
G
G
G的最大完全子图。
输入格式
输入第一行读入两个整数
n
,
d
n,d
n,d 分别表示
∣
V
∣
|V|
∣V∣ 和连边的距离限制。
接下来
n
n
n 行,第
i
i
i 行两个整数表示点权。
输出格式
输出一个数表示最大的大小。
样例
输入
4 1
1 1
2 1
1 1
2 2
输出
3
解释
点
1
,
2
,
3
1,2,3
1,2,3 之间构成了一个团。(对应点权分别是(1,1),(2,1),(1,1))
而由于点1和点4之间不存在边(|1-2|+|1-2|=2>1),所以最大团的点数只能是3。
数据范围
测试点 | n | x,y,d |
---|---|---|
1,2 | 1 ≤ n ≤ 10 1≤n≤10 1≤n≤10 | 1 ≤ x , y , d ≤ 100 1≤x,y,d≤100 1≤x,y,d≤100 |
3,4 | 1 ≤ n ≤ 500 1≤n≤500 1≤n≤500 | 1 ≤ x , y , d ≤ 1 0 3 1≤x,y,d≤10^3 1≤x,y,d≤103 |
5,6,7,8,9,10 | 1 ≤ n ≤ 3 ∗ 1 0 5 1≤n≤3*10^5 1≤n≤3∗105 | 1 ≤ x , y , d ≤ 1 0 8 1≤x,y,d≤10^8 1≤x,y,d≤108 |
题解
曼哈顿转切比雪夫
题目中要求的其实是一个图形中最远的两个点距离小于等于d的,所以去框它的时候其实是一个斜了
45
°
45\degree
45°的正方形(后面称它为菱形),这样很不方便处理,那么我们考虑使用人类智慧旋转
45
°
45\degree
45°。
(
x
,
y
)
(x,y)
(x,y)—>
(
x
−
y
,
x
+
y
)
(x-y,x+y)
(x−y,x+y)
扫描线
就直接用扫描线的模板,注意里面的线段树要改成维护区间最大值的,pushup要把 > 0 >0 >0改成 ! = 0 !=0 !=0.
代码实现
//Gmt丶FFF 的集合 g
#pragma GCC optimize(3)
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
using namespace std;
int n,d;
int ans;
int lazy[2500010];//标记了这条线段出现的次数
int s[2500010];
struct w{
int x,y1,y2,flag;
}a[2500010];//坐标
struct node{
int l,r;
int sum;
}cl[2500010];//线段树
void in(int &x){
int nt;
x=0;
while(!isdigit(nt=getchar()));
x=nt^'0';
while(isdigit(nt=getchar())){
x=(x<<3)+(x<<1)+(nt^'0');
}
}
bool cmp(w x,w y){
return x.x==y.x?x.flag>y.flag:x.x<y.x;
}
void pushup(int rt){
if(lazy[rt]!=0){
lazy[rt<<1]+=lazy[rt];
lazy[(rt<<1)|1]+=lazy[rt];
cl[rt<<1].sum+=lazy[rt];
cl[(rt<<1)|1].sum+=lazy[rt];
lazy[rt]=0;
}
}
void build(int rt,int l,int r){
if(r-l>1){
cl[rt].l=s[l];
cl[rt].r=s[r];
build((rt<<1),l,(l+r)>>1);
build(((rt<<1)|1),((l+r)>>1),r);
}
else{
cl[rt].l=s[l];
cl[rt].r=s[r];
}
return;
}
void update(int rt,int y1,int y2,int flag){
if(cl[rt].l==y1&&cl[rt].r==y2){
lazy[rt]+=flag;
cl[rt].sum+=flag;
}
else{
pushup(rt);
if(cl[(rt<<1)].r>y1){
update((rt<<1),y1,min(cl[(rt<<1)].r,y2),flag);
}
if(cl[((rt<<1)|1)].l<y2){
update(((rt<<1)|1),max(cl[((rt<<1)|1)].l,y1),y2,flag);
}
cl[rt].sum=max(cl[rt<<1].sum,cl[(rt<<1)|1].sum);
}
}
int main(){
register int i;
in(n),in(d);
for(i=0;i<n;++i){
in(a[i].x),in(a[i].y1);
}
for(i=0;i<n;++i){
a[i].x=a[i].x-a[i].y1;
a[i].y1=a[i].x+(a[i].y1<<1);
a[i+n].y2=a[i].y2=a[i].y1+d+1;
a[i].flag=1;
a[i+n].x=a[i].x+d;
a[i+n].y1=a[i].y1;
a[i+n].flag=-1;
s[i+1]=a[i].y1;
s[i+n+1]=a[i].y2;
}//曼哈顿转切比雪夫
sort(s+1,s+(n<<1)+1);//离散化
sort(a,a+(n<<1),cmp);//把矩形的边按横坐标从小到大排序
build(1,1,(n<<1));
update(1,a[0].y1,a[0].y2,a[0].flag);
for(i=1;i<(n<<1);++i){
ans=max(ans,cl[1].sum);
update(1,a[i].y1,a[i].y2,a[i].flag);
}
printf("%d",ans);
return 0;
}