[GDKOI2018/T30750]基站

版权声明:转载注明出处,部分带(坑)文章谢绝转载。 https://blog.csdn.net/linjiayang2016/article/details/84637141

题目大意

平面上有 n n 个基站,每个基站有 3 3 种可能的类型。若一种类型的基站恰好是另一种类型的两个基站的中点,则该基站会受到那对基站的干扰。求每个基站受到的干扰数。
数据范围
对于 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
对于 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
对于 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
对于 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
对于 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

题解

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

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

//O2下AC
#include<bits/stdc++.h>
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;
}

猜你喜欢

转载自blog.csdn.net/linjiayang2016/article/details/84637141