[hdu4609]计数方法,FFT

题目:给一个数组a,从里面任选三个数,求以这三个数为三条边能构成三角形的概率。

思路:由于每个数只能用一次,所以考虑枚举三边中的最大边。先将a数组排序,然后枚举它的每个数x作为最大边,那么问题就是要求在数组a剩余的数里面“找小于等于x”且“和大于x”的数对个数,答案显然不能直接得到。不妨先计算这样一个数组ans[i]:表示在数组a里面有放回的选两个数,和为i的数对个数。设cnt[i]为i这个数在a数组里面出现的次数,那么ans相当于cnt对cnt的卷积结果, 这可以利用FFT在nlogn的时间内求得。ans数组出来以后,由于还需要将它变成“无放回地取两个数”的结果,且要保证答案的无序性(由于每个答案都会被统计两次,全部除以2即可),需要做些处理,具体见代码。处理完后,ans[i]表示从a数组里面任选两个数和为i的方案数,就可以利用ans来得到答案了。

先对ans进行前缀和处理(ans[i]+=ans[i-1]),在枚举到最大边x时,另两边的和可以为x+1~maxsum的任意值,将答案加上ans[maxsum] - ans[x]---(1),由于加的这个答案里面包含很多不合法的,需要一一减去,下面对不合法的进行分类{设共n个数,范围为[a,a+n),现在枚举到了i这个位置,数为x,需要注意的是由于已经排序,下面的大小关系是指位置的大小关系,而不用特别去考虑相等情况了}:

(1)两边都大于x,显然它们的和大于x,所以会出现在(1)里面,需要减去: (n-i-1)*(n-i-2)/2

(2)一边大于x,一边小于x,显然它们的和也大于x,会出现在(1)里面,需要减去:i*(n-i-1)

(3)一边等于x,一边不等于x,显然他们的和也大于x,会出现在(1)里面,需要减去:n-1

这样枚举完所有数,就得到答案了。

另外,整体看减去的数,是一个常数,而枚举每个数累加的ans值跟次序无关 ,于是根本无需排序,一样得到正确的结果。


1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

70

71

72

73

74

75

76

77

78

79

80

81

82

83

84

85

86

87

88

89

90

91

92

93

94

95

96

97

98

99

100

101

102

103

104

105

106

107

108

109

110

111

112

113

114

115

116

117

118

119

120

121

122

123

124

125

126

127

128

129

130

131

132

133

134

135

136

137

138

139

140

141

142

143

144

145

146

147

148

149

150

151

152

153

154

155

156

157

158

159

/* ******************************************************************************** */

#include <iostream>                                                                 //

#include <cstdio>                                                                   //

#include <cmath>                                                                    //

#include <cstdlib>                                                                  //

#include <cstring>                                                                  //

#include <vector>                                                                   //

#include <ctime>                                                                    //

#include <deque>                                                                    //

#include <queue>                                                                    //

#include <algorithm>                                                                //

#include <map>                                                                      //

#include <cmath>                                                                    //

using namespace std;                                                                //

                                                                                    //

#define pb push_back                                                                //

#define mp make_pair                                                                //

#define X first                                                                     //

#define Y second                                                                    //

#define all(a) (a).begin(), (a).end()                                               //

#define fillchar(a, x) memset(a, x, sizeof(a))                                      //

                                                                                    //

void RI(vector<int>&a,int n){a.resize(n);for(int i=0;i<n;i++)scanf("%d",&a[i]);}    //

void RI(){}void RI(int&X){scanf("%d",&X);}template<typename...R>                    //

void RI(int&f,R&...r){RI(f);RI(r...);}void RI(int*p,int*q){int d=p<q?1:-1;          //

while(p!=q){scanf("%d",p);p+=d;}}void print(){cout<<endl;}template<typename T>      //

void print(const T t){cout<<t<<endl;}template<typename F,typename...R>              //

void print(const F f,const R...r){cout<<f<<", ";print(r...);}template<typename T>   //

void print(T*p, T*q){int d=p<q?1:-1;while(p!=q){cout<<*p<<", ";p+=d;}cout<<endl;}   //

                                                                                    //

typedef pair<intint> pii;                                                         //

typedef long long ll;                                                               //

typedef unsigned long long ull;                                                     //

                                                                                    //

template<typename T>bool umax(T&a, const T&b){return b<=a?false:(a=b,true);}        //

template<typename T>bool umin(T&a, const T&b){return b>=a?false:(a=b,true);}        //

template<typename T>                                                                //

void V2A(T a[],const vector<T>&b){for(int i=0;i<b.size();i++)a[i]=b[i];}            //

template<typename T>                                                                //

void A2V(vector<T>&a,const T b[]){for(int i=0;i<a.size();i++)a[i]=b[i];}            //

                                                                                    //

const double PI = acos(-1);                                                         //

                                                                                    //

/* -------------------------------------------------------------------------------- */

namespace FFT {

    const static int maxn = 1e5 + 7;

    #define L(x) (1 << (x))

    double ax[maxn << 2], ay[maxn << 2], bx[maxn << 2], by[maxn << 2];//需要四倍空间

    int revv(int x, int bits) {

        int ret = 0;

        for (int i = 0; i < bits; i++) {

            ret <<= 1;

            ret |= x & 1;

            x >>= 1;

        }

        return ret;

    }

    void fft(double * a, double * b, int n, bool rev) {

        int bits = 0;

        while (1 << bits < n) ++bits;

        for (int i = 0; i < n; i++) {

            int j = revv(i, bits);

            if (i < j)

                swap(a[i], a[j]), swap(b[i], b[j]);

        }

        for (int len = 2; len <= n; len <<= 1) {

            int half = len >> 1;

            double wmx = cos(2 * PI / len), wmy = sin(2 * PI / len);

            if (rev) wmy = -wmy;

            for (int i = 0; i < n; i += len) {

                double wx = 1, wy = 0;

                for (int j = 0; j < half; j++) {

                    double cx = a[i + j], cy = b[i + j];

                    double dx = a[i + j + half], dy = b[i + j + half];

                    double ex = dx * wx - dy * wy, ey = dx * wy + dy * wx;

                    a[i + j] = cx + ex, b[i + j] = cy + ey;

                    a[i + j + half] = cx - ex, b[i + j + half] = cy - ey;

                    double wnx = wx * wmx - wy * wmy, wny = wx * wmy + wy * wmx;

                    wx = wnx, wy = wny;

                }

            }

        }

        if (rev) {

            for (int i = 0; i < n; i++)

                a[i] /= n, b[i] /= n;

        }

    }

    int solve(ll a[], int na, ll b[], int nb, ll ans[]) {

        int len = max(na, nb), ln;

        for(ln = 0; L(ln) < len; ++ln);

        len = L(++ln);

        for (int i = 0; i < len ; ++i) {

            if (i >= na) ax[i] = 0, ay[i] = 0;

            else ax[i] = a[i], ay[i] = 0;

        }

        fft(ax, ay, len, 0);

        for (int i = 0; i < len; ++i) {

            if (i >= nb) bx[i] = 0, by[i] = 0;

            else bx[i] = b[i], by[i] = 0;

        }

        fft(bx, by, len, 0);

        for (int i = 0; i < len; ++i) {

            double cx = ax[i] * bx[i] - ay[i] * by[i];

            double cy = ax[i] * by[i] + ay[i] * bx[i];

            ax[i] = cx, ay[i] = cy;

        }

        fft(ax, ay, len, 1);

        for (int i = 0; i < len; ++i)

            ans[i] = (ll)(ax[i] + 0.5);

        return len;

    }

    #undef L(x)

}

const int maxn = 1e5 + 7;

ll c[maxn << 2], d[maxn << 2], ans[maxn << 2], sum[maxn << 2];

int a[maxn];

int main() {

#ifndef ONLINE_JUDGE

    freopen("in.txt""r", stdin);

#endif // ONLINE_JUDGE

    int T, n;

    cin >> T;

    while (T --) {

        fillchar(c, 0);

        fillchar(d, 0);

        fillchar(ans, 0);

        cin >> n;

        int maxv = 0;

        for (int i = 0; i < n; i ++) {

            RI(a[i]);

            c[a[i]] ++;

            umax(maxv, a[i]);

        }

        maxv ++;

        for (int i = 0; i < maxv; i ++) d[i] = c[i];

        int L = FFT::solve(c, maxv, d, maxv, ans);

        while (ans[L] <= 0 && L > 1) L --;

        L ++;

        for (int i = 0; i < n; i ++) ans[a[i] << 1] --;

        for (int i = 1; i < L; i ++) {

            ans[i] >>= 1;

            sum[i] = sum[i - 1] + ans[i];

        }

        ll Result = 0, Total = (ll)n * (n - 1) * (n - 2) / 6;

        //sort(a, a + n);

        for (int i = 0; i < n; i ++) {

            ll buf = sum[L - 1] - sum[a[i]];

            buf -= n - 1;

            buf -= (ll)i * (n - i - 1);

            buf -= (ll)(n - i - 1) * (n - i - 2) / 2;

            Result += buf;

        }

        printf("%.7f\n", (double)Result / Total);

    }

    return 0;

}

/* ******************************************************************************** */

时间: 2024-10-05 08:09:38

[hdu4609]计数方法,FFT的相关文章

数学基础——基本计数方法

计数方法最基础的两个原理是:加法原理和乘法原理. 容斥原理: 假设一个班里有10个学生喜欢数学,15个学生喜欢语文,21个学生喜欢编程.那么班级总人数: |A∪B∪C| = |A| + |B| + |C| - |A∩B| - |A∩C| - |B∩C| + |A∩B∩C| 一般的,任意多个集合,集合内的元素个数为奇数,前面的符号为正. 问题1:排列问题 n个不同的数,选k个排成1排,有多少种排法. 答案计做p(n,k) = n*(n-1)*(n-2)*...*(n-(k-1)) = n!/(n-

学习总结--数学.基本计数方法

学习总结--数学.基本计数方法 一.计数方法的原理 1.加法原理:做一件事情有n中办法,第i种办法有pi种执行方案,那么总的解决这件事情的方案数即为p1+p2+p3+...+pn. 2.乘法原理:做一件事情分为n个步骤,第i个步骤的执行方案有pi种,则一共有p1?p2?p3?...?pn种方案解决该问题. 3.容斥原理:一个班级有,集合A的人喜欢数学,集合B的人喜欢英语,结合C的人喜欢语文,那么该班级的人数应该是多少? 如果我们将三个集合的人数相加起来,那么就重复计算了既喜欢数学又喜欢英语的.既

【组合数学】 05 - 经典计数方法

1. 基本计数的母函数 现在来用母函数来求解基本计数问题,母函数既可以完成自动计数,还能表示计数本身,像Stirling数这种就只能用母函数表示.自动计数适用于可以分步的计数问题,并且目标值是每步值之和,这与多项式的运算性质有关. 1.1 组合数和分划数 直观上最符合这一特点的就是模型2,从\(n\)个可区别对象中选出\(m个\).限制第\(k\)个对象被取的次数在集合\(M_k\)中,它被选情况的母函数是\(\sum\limits_{i\in M_k}x^i\),所有元素被选择的情况可以借助母

算法竞赛入门经典行训练指南【计数方法】------2015年1月23日

基础知识整理: (1)加法原理 (2)乘法原理 (3)容斥原理(注意变式问题) (4)排列组合公式的应用及变形: 排列的公式: 其变形为: 与组合的关系如下(以下第一个公式很重要): 排列组合公式的重要推论: 推论1: 对于第一个物体如果不取的话,那么我们有C(n,k+1)种方法,对于第一个物体取的话,我有C(n,k)种方法.公式得证. 推论2: 这可以降低求解二项式系数的时间复杂度,通过利用递推关系自小到大依次计算得出,方便快捷. (5)排列组合的基本问题: Q1:求有重复元素的排列. 有k个

php导出excel长数字串显示为科学计数方法与最终解决方法

1.设置单元格为文本 $objPHPExcel = new PHPExcel(); $objPHPExcel->setActiveSheetIndex(0); $objPHPExcel->getActiveSheet()->setTitle('Simple');//设置A3单元格为文本 $objPHPExcel->getActiveSheet()->getStyle('A3')->getNumberFormat()->setFormatCode(PHPExcel_

算法竞赛训练指南2.1 计数方法

1. O(n)方法求C(n,m) 利用公式C(n,k+1)=C(n,k)*(n-k)/(k+1) 模板: #include <iostream> #include <algorithm> using namespace std; typedef unsigned long long LL; const int maxn=100005; LL n,m; LL C() { if(m==0||n==m) return 1; if(m>n-m) m=n-m; LL ans,temp=

计数方法,博弈论(扫描线,树形SG):HDU 5299 Circles Game

There are n circles on a infinitely large table.With every two circle, either one contains another or isolates from the other.They are never crossed nor tangent.Alice and Bob are playing a game concerning these circles.They take turn to play,Alice go

计数方法(扫描线):JLOI 2016 圆的异或并

Description 在平面直角坐标系中给定N个圆.已知这些圆两两没有交点,即两圆的关系只存在相离和包含.求这些圆的异或面 积并.异或面积并为:当一片区域在奇数个圆内则计算其面积,当一片区域在偶数个圆内则不考虑. Input 第一行包含一个正整数N,代表圆的个数.接下来N行,每行3个非负整数x,y,r,表示一个圆心在(x,y),半径为r的 圆.保证|x|,|y|,≤10^8,r>0,N<=200000 Output 仅一行一个整数,表示所有圆的异或面积并除以圆周率Pi的结果. Sample

【基本计数方法---加法原理和乘法原理】UVa 11538 - Chess Queen

题目链接 题意:给出m行n列的棋盘,当两皇后在同行同列或同对角线上时可以互相攻击,问共有多少种攻击方式. 分析:首先可以利用加法原理分情况讨论:①两皇后在同一行:②两皇后在同一列:③两皇后在同一对角线( / 或 \ ): 其次利用乘法原理分别讨论: ①同一行时(A),先选某一行某一列放置其中一个皇后,共m*n种情况:其次在选出的这一行里的其他n-1个位置中选一个放另一个皇后:共m*n*(n-1)种情况: ②同一列时(B)情况相同,为n*m*(m-1)种情况: ③同一对角线(D)上时,先讨论 /