hdu4609 3-idiots

链接

http://acm.hdu.edu.cn/showproblem.php?pid=4609

题解

我发现凡是涉及到精度的题目,就会让人有一种"会不会是因为精度导致了 W A \color{red}WA WA"的疑虑,其实如果认为自己的算法没问题的话,可以适当打消这种疑虑,否则会影响的速度,赛场上是有消极影响的
我先搞一个“桶”,统计每种数字的出现次数 c n t i cnt_i cnti,把 c n t cnt cnt数组看作一个多项式的系数向量,进行多项式卷积
然后就得到了每种权值可以由多少种组合得到(减去重复的情况),设其为 c n t 2 i cnt2_i cnt2i
然后枚举最大边 c c c
现在我就是要统计,有多少种选法,使得最大边为 c c c,且较小的两条边 a , b a,b a,b满足 a + b > c a+b>c a+b>c
容易发现 a &gt; 0 , b &gt; 0 a&gt;0,b&gt;0 a>0,b>0所以当 a , b ≤ c a,b\leq c a,bc时会有很好的性质“ a &lt; c a&lt;c a<c b &lt; c b&lt;c b<c”,但是当 a + b &gt; c a+b&gt;c a+b>c时却容易把“ a &gt; c a&gt;c a>c b &gt; c b&gt;c b>c”的情况给统计进去,所以我可以容斥一下,去统计 a , b ≤ c a,b\leq c a,bc的情况,最后再用总数减去
显然 a , b ≤ c a,b\leq c a,bc a ≤ c , b ≤ c a\leq c,b\leq c ac,bc的数量就等于 ∑ i = 1 c c n t 2 i \sum_{i=1}^ccnt2_i i=1ccnt2i
a , b ≤ c a,b\leq c a,bc的选择方法数,可以分三种情况累加:
不妨设 c c c x x x
情况一:选择三个 c c c C x 3 C_{x}^3 Cx3
情况二:选两个 c c c C x 2 ∑ i = 1 c − 1 c n t i C_{x}^2\sum_{i=1}^{c-1}cnt_i Cx2i=1c1cnti
情况三:选一个 c c c C x 1 ∑ i = 1 c − 1 ( c n t i ) ( ( ∑ i = 1 c − 1 c n t i ) − 1 ) 2 C_{x}^1\frac{\sum_{i=1}^{c-1}(cnt_i)((\sum_{i=1}^{c-1}cnt_i)-1)}{2} Cx12i=1c1(cnti)((i=1c1cnti)1)

代码

#include <bits/stdc++.h>
#define maxn 300010
#define cl(x) memset(x,0,sizeof(x))
using namespace std;
typedef long long ll;
struct comp
{
    double x, y;
    comp(double x, double y):x(x),y(y){}
    comp():x(0),y(0){}
    comp operator+(comp t){return comp(x+t.x, y+t.y);}
    comp operator-(comp t){return comp(x-t.x, y-t.y);}
    comp operator*(comp t){return comp(x*t.x-y*t.y, x*t.y+t.x*y);}
}a[maxn], b[maxn], c[maxn];
struct FFT
{
    int up, R[maxn];
    const double pi=acos(-1);
    void init(int bound)    //bound是积多项式的最高次幂
    {
        int L(0);
        for(up=1;up<=bound;up<<=1,L++);
        for(int i=0;i<up;i++)R[i]=(R[i>>1]>>1)|((i&1)<<(L-1));
    }
    void fft(comp* a, int opt)
    {
        int i, j, k;
        comp wn, w, x, y;
        for(i=0;i<up;i++)if(i>R[i])swap(a[i],a[R[i]]);
        for(i=1;i<up;i<<=1)
        {
            wn=comp(cos(pi/i),opt*sin(pi/i));
            for(j=0;j<up;j+=i<<1)
                for(w=comp(1,0),k=0;k<i;k++,w=w*wn)
                {
                    x=a[k+j], y=a[k+j+i]*w;
                    a[k+j]=x+y, a[k+j+i]=x-y;
                }
        }
        if(opt==-1)for(i=0;i<up;i++)a[i].x/=up;
    }
    void juan(comp* a, comp* b, comp* c)
    {
        fft(a,1), fft(b,1);
        for(int i=0;i<up;i++)c[i]=a[i]*b[i];
        fft(c,-1);
    }
}fft;
ll read(ll x=0)
{
    ll c, f(1);
    for(c=getchar();!isdigit(c);c=getchar())if(c=='-')f=-f;
    for(;isdigit(c);c=getchar())x=x*10+c-48;
    return f*x;
}
ll cnt[maxn], cnt2[maxn];
int main()
{
    ll T(read()), N, i, j, ans, mx;
    while(T--)
    {
        cl(cnt), cl(cnt2), cl(a), cl(b), cl(c), ans=0, mx=0;
        N=read();
        for(i=1;i<=N;i++)
        {
            auto x=read();
            mx=max(mx,x);
            cnt[x]++;
        }
        for(i=1;i<=mx;i++)a[i].x=cnt[i], b[i]=a[i];
        fft.init(mx*2);
        fft.juan(a,b,c);
        for(i=1;i<=mx;i++)c[2*i].x-=cnt[i];
        for(i=1;i<=mx;i++)cnt2[i]=ll(c[i].x+0.5)/2;
        for(i=1;i<=mx;i++)cnt[i]+=cnt[i-1], cnt2[i]+=cnt2[i-1];
        for(i=1;i<=mx;i++)
        {
            auto x=cnt[i]-cnt[i-1];
            ans += x*cnt[i-1]*(cnt[i-1]-1)/2 + x*(x-1)/2*cnt[i-1] + x*(x-1)*(x-2)/6;
            ans -= cnt2[i]*x;
        }
        printf("%.7lf\n",double(6*ans)/(N*(N-1)*(N-2)));
    }
    return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值