[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< int , int > 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; } /* ******************************************************************************** */ |