[GDKOI2018/T30750]基站

题目大意

平面上有 n n n 个基站,每个基站有 3 3 3 种可能的类型。若一种类型的基站恰好是另一种类型的两个基站的中点,则该基站会受到那对基站的干扰。求每个基站受到的干扰数。
数据范围
对于 10 % 10\% 10% 的数据, 1 ⩽ n ⩽ 1 0 2 , 0 < x i , y i < 200 1\leqslant n\leqslant 10^2,0<x_i,y_i<200 1n102,0<xi,yi<200
对于 30 % 30\% 30% 的数据, 1 ⩽ n ⩽ 5 × 1 0 3 , 0 < x i , y i < 200 1\leqslant n\leqslant 5\times10^3,0<x_i,y_i<200 1n5×103,0<xi,yi<200
对于 50 % 50\% 50% 的数据, 1 ⩽ n ⩽ 4 × 1 0 4 , 0 < x i , y i < 300 1\leqslant n\leqslant 4\times10^4,0<x_i,y_i<300 1n4×104,0<xi,yi<300
对于 70 % 70\% 70% 的数据, 1 ⩽ n ⩽ 6 × 1 0 4 , 0 < x i , y i < 400 1\leqslant n\leqslant 6\times 10^4,0<x_i,y_i<400 1n6×104,0<xi,yi<400
对于 100 % 100\% 100% 的数据, 1 ⩽ n ⩽ 1 0 5 , 0 < x i , y i < 500 , 0 ⩽ k ⩽ 2 1\leqslant n\leqslant 10^5,0<x_i,y_i<500,0\leqslant k\leqslant 2 1n105,0<xi,yi<500,0k2

题解

首先,暴力是很好打的,三十分到手。可惜的是,许多人只拿了三十分。
因为五十分也是可以做的,先对基站分类,然后按照基站的类型做三次暴力,如果你有非常小心常数的习惯,你是完全可以卡过去的。
成 绩 单 . j p g ( 坑 ) 成绩单.jpg(坑) .jpg()

下面来说正解。
注意到 x i x_i xi y i y_i yi 非常小,因此我们可以转换为一维问题。
z i = x i ∗ 1000 + y i z_i=x_i*1000+y_i zi=xi1000+yi,则条件" A A A B , C B,C B,C 的中点"的条件与 z A ∗ 2 = z B + z C z_A*2=z_B+z_C zA2=zB+zC 等价。
充分性证明:
∵ A \because A A B , C B,C B,C 的中点
∴ x A ∗ 2 = x B + x C , x A ∗ 2 = x B + x C \therefore x_A*2=x_B+x_C,x_A*2=x_B+x_C xA2=xB+xC,xA2=xB+xC
∴ z A ∗ 2 = z B + z C \therefore z_A*2=z_B+z_C zA2=zB+zC
必要性证明:
∵ z A ∗ 2 = z B + z C ( 1 ) \because z_A*2=z_B+z_C\qquad(1) zA2=zB+zC(1)
∴ z A ∗ 2 ≡ z B + z C ( m o d 1000 ) \therefore z_A*2≡z_B+z_C\pmod {1000} zA2zB+zC(mod1000)
∴ y A ∗ 2 = y B + y C ( 2 ) \therefore y_A*2=y_B+y_C\qquad(2) yA2=yB+yC(2)
( 1 ) (1) (1)式减去 ( 2 ) (2) (2)式得: x A ∗ 2 = x B + x C x_A*2=x_B+x_C xA2=xB+xC
接下来,问题变为:直线上有 n n n 个基站,每个基站有 3 3 3 种可能的类型。若一种类型的基站恰好是另一种类型的两个基站的中点,则该基站会受到那对基站的干扰。求每个基站受到的干扰数。下面只考虑类型为0的基站,其余的类似。
构造序列 A i = [ k i = 0 ] A_i=[k_i=0] Ai=[ki=0],然后计算序列 S = A ∗ A S=A*A S=AA
由卷积的定义式可得: S i = ∑ A i ∗ A n − i S_i=\sum A_i*A_{n-i} Si=AiAni
i i i 为奇数的时候, S i S_i Si 由两个和为奇数的位置组成,它们的中点不在整数点上,因此可以忽略。
而只有当 i i i 为偶数的时候,产生贡献的两个点才会在整数点上,而且会被重复算两次,因此最后统计答案的时候要除以 2 2 2

//O2下AC
#include
using namespace std;
const int maxn=2097153;
const double pi=acos(-1.0);
struct comp{
    double x,y;
    comp(double xx=0,double yy=0):x(xx),y(yy) {}
    friend comp operator+(const comp &x,const comp &y) {return comp(x.x+y.x,x.y+y.y);}
    friend comp operator-(const comp &x,const comp &y) {return comp(x.x-y.x,x.y-y.y);}
    friend comp operator*(const comp &a,const comp &b) {return comp(a.x*b.x-a.y*b.y,a.x*b.y+b.x*a.y);}
}a[maxn],b[maxn],c[maxn];
int limit=1,l=0,r[maxn];
int reset(int n,int m){
	while(limit<=n+m)
        limit<<=1,++l;
    for(int i=1;i<limit;i++)
        r[i]=((r[i>>1]>>1)|((i&1)<<(l-1)));
}
void fft(comp *t,int ty){
    for(int i=0;i<limit;i++)
        if(i<r[i])
            swap(t[i],t[r[i]]);
    for(int mid=1;mid<limit;mid<<=1){
        comp wn(cos(pi/mid),ty*sin(pi/mid));
        for(int j=0,R=(mid<<1);j<limit;j+=R){
            comp w(1,0);
            for(int k=0;k<mid;k++,w=w*wn){
                comp x=t[j+k],y=w*t[j+k+mid];
                t[j+k]=x+y;
                t[j+k+mid]=x-y;
            }
        }
    }
}
#define Maxn 510000
int n,pos[Maxn],d[Maxn];
int ts[Maxn],pd[Maxn],mx;
void dos(comp *a,int k){
	fft(a,1);for(int i=0;i<limit;i++) a[i]=a[i]*a[i]; fft(a,-1);
    for(int i=0;i<=mx*2;i+=2){
    	if(pd[i/2]==k||pd[i/2]==0) continue;
    	ts[i/2]+=(int)floor(a[i].x/limit+0.001)/2;
    }
}
int main(void)
{
    scanf("%d",&n);
    for(int i=1,x,y;i<=n;i++){
    	scanf("%d%d%d",&x,&y,&d[i]);
    	pd[pos[i]=x*1000+y]=++d[i];
    	mx=max(mx,pos[i]);
    }
    for(int i=1;i<=n;i++){
    	if(d[i]==1) a[pos[i]].x=1;
    	if(d[i]==2) b[pos[i]].x=1;
    	if(d[i]==3) c[pos[i]].x=1;
    }
    reset(mx+1,mx+1);
	dos(a,1);dos(b,2);dos(c,3);
    for(int i=1;i<=n;i++)
    	printf("%d\n",ts[pos[i]]);
    return 0;
}

你可能感兴趣的:(临时文件)