[JZOJ4330] 清华集训模拟几何题

Posted jz-597

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了[JZOJ4330] 清华集训模拟几何题相关的知识,希望对你有一定的参考价值。

题目

题目大意

也懒得解释题目大意了……


正解

正解居然是\(FFT\)
不要看题目的那个式子这么长,也不要在那个式子上下手。
其实我们会发现,不同的\((x_i-x_j,y_i-y_j,z_i-z_j)\)并不多。
如果我们求出每个三元组的出现次数,后面的就好做了。
那怎么求呢?
祭出我们的大杀器——\(FFT\)
考虑只有一个维怎么做。设两个多项式分别为\(A\)\(B\)
对于\(x_i\),就在\(A\)\(x_i\)这一位上的系数加一;
对于\(x_j\),就在\(B\)\(77-x_j\)这一位上的系数加一。
\(A\)\(B\)乘起来,那么\(77+x_i-x_j\)就是差\(x_i-x_j\)对应的个数。
对于三维,就将这三个数压成一维的就好了。

实际上也可以用NTT。仔细分析一下,就可以发现每个三元组的出现次数肯定是不超过\(998244353\)的。


正解

using namespace std;
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cassert>
#include <cmath>
#define N 1000000
#define MX 3652264
#define mo 998244353
inline int input()
    char ch=getchar();
    while (ch<'0' || '9'<ch)
        ch=getchar();
    int x=0;
    do
        x=x*10+ch-'0';
        ch=getchar();
    
    while ('0'<=ch && ch<='9');
    return x;

inline int my_pow(int x,int y)
    int res=1;
    for (;y;y>>=1,x=(long long)x*x%mo)
        if (y&1)
            res=(long long)res*x%mo;
    return res;

inline int pow4(int x)x*=x;return x*x;
#define M (1<<22)
#define bit 22
int n;
struct DOT
    int x,y,z;
    inline DOT rev()return 77-x,77-y,77-z;
 d[N];
inline int pia(const DOT &a)return (a.x*154+a.y)*154+a.z;
int a[1<<22],b[1<<22],cnt[1<<22];
int rev[1<<22];
inline void ntt(int *a,int flag)
    for (int i=0;i<M;++i)
        if (i<rev[i])
            swap(a[i],a[rev[i]]);
    for (int i=1;i<M;i<<=1)
        int wn=my_pow(3,(mo+1)/(i<<1));
        if (flag==-1)
            wn=my_pow(wn,mo-2);
        for (int j=0;j<M;j+=i<<1)
            int wnk=1;
            for (int k=j;k<j+i;++k,wnk=(long long)wnk*wn%mo)
                int x=a[k],y=(long long)wnk*a[k+i]%mo;
                a[k]=(x+y>=mo?x+y-mo:x+y);
                a[k+i]=(x-y<0?x-y+mo:x-y);
            
        
    
    if (flag==-1)
        int invm=my_pow(M,mo-2);
        for (int i=0;i<M;++i)
            a[i]=(long long)a[i]*invm%mo;
    

inline void multi(int *a,int *b,int *c)
    for (int i=1;i<M;++i)
        rev[i]=rev[i>>1]>>1|(i&1)<<bit-1;
    ntt(a,1),ntt(b,1);
    for (int i=0;i<M;++i)
        c[i]=(long long)a[i]*b[i]%mo;
    ntt(c,-1);

DOT back[M];
int main()
    freopen("geometry.in","r",stdin);
    freopen("geometry.out","w",stdout);
    int Q;
    scanf("%d%d",&n,&Q);
    for (int i=1;i<=n;++i)
        d[i]=input(),input(),input();
    for (int i=1;i<=n;++i)
        a[pia(d[i])]++;
        b[pia(d[i].rev())]++;
    
    multi(a,b,cnt);
    for (int i=0;i<MX;++i)
        int j=i;
        back[i].z=j%154-77;j/=154;
        back[i].y=j%154-77;j/=154;
        back[i].x=j-77;
//      assert(pia(back[i])==i);
    
    while (Q--)
        int a=input(),b=input(),c=input(),d=input();
        double ans=0;
        for (int i=0;i<MX;++i)
            if (cnt[i] && (back[i].x|back[i].y|back[i].z))
                ans+=(long long)cnt[i]*abs(a*back[i].x+b*back[i].y+c*back[i].z+d)/sqrt(pow4(back[i].x)+pow4(back[i].y)+pow4(back[i].z));
        ans/=(long long)n*(n-1);
        printf("%.10lf\n",ans);
    
    return 0;

总结

\(FFT\)\(NTT\)真是个bug般的存在……

以上是关于[JZOJ4330] 清华集训模拟几何题的主要内容,如果未能解决你的问题,请参考以下文章

清华集训2014 做题记录

2017.11.26清华集训2017模拟

[清华集训]序列操作

「清华集训2014」主旋律

清华集训2016-组合数问题

清华集训2016温暖会指引我们前行