1. 程式人生 > >【 bzoj 3509 】[CodeChef] COUNTARI

【 bzoj 3509 】[CodeChef] COUNTARI

  看起來很像資料結構亂搞?
  然而仔細看看資料範圍:Ai<=30000
  這意味著我們可以用生成函式來亂搞。
  將式子變成Ai+Ak=2Aj,於是就可以很愉快地列舉j,記錄j兩邊的生成函式,然後求卷積
  就行了
  ……?
  毛。。。這樣卷積是O(nVlogV)的   //以下設V=max{A}
  怎樣降低FFT次數呢?
  考慮分塊。假設分成了B塊,塊的大小是L
  還是列舉j,考慮i在塊內的情況,可以很顯然地預處理對應數出現次數來搞。k在塊內的時候同理。注意這裡有一些重複的要減去。然後i,k都在塊外就用FFT來搞。
  這樣時間複雜度是O(BVlogV+(B+

L)V)
  由於上面這個式子的存在,塊不能設太小,不然B就會太大,FFT次數會增多,大概2000左右就差不多了。
  用了某個方法稍微優化了一下FFT的常數(不過應該是損耗了一點點精度的吧)。
  

#include <bits/stdc++.h>
using namespace std;
#define rep(i,a,b) for(int i = a , _ = b ; i <= _ ; i ++)
#define per(i,a,b) for(int i = a , _ = b ; i >= _ ; i --)
#define For(i,a,b) for(int i = a , _ = b ; i <  _ ; i ++)
inline int rd() { char c = getchar(); while (!isdigit(c)) c = getchar() ; int x = c - '0'; while (isdigit(c = getchar())) x = x * 10 + c - '0'; return x; } inline void upmax(int&a , int b) { if (a < b) a = b; } inline void upmin(int&a , int b) { if (a > b) a = b; } typedef
long long ll; const int maxn = 110007; const int maxs = 61007; const int maxb = 307; const int len = 1823; typedef int arr[maxn]; typedef int blk[maxb]; typedef int num[maxs]; arr a , belong , L , R; blk st , ed; num cnt[maxb] , cnt_nxt , cnt_pre; ll ans; int n , tot , lim; void input() { n = rd(); rep (i , 1 , n) a[i] = rd() , upmax(lim , a[i]); } inline void init_block() { rep (i , 1 , n) belong[i] = (i - 1) / len + 1; tot = belong[n]; rep (i , 1 , tot) st[i] = (i - 1) * len + 1 , ed[i] = i * len; upmin(ed[tot] , n); rep (i , 1 , tot) { rep (j , st[i] , ed[i]) cnt[i][a[j]] ++; } } const int maxN = 524299; struct comp { long double real , imag; comp(long double real = 0 , long double imag = 0):real(real) , imag(imag) { } inline friend comp operator+(comp&a , comp&b) { return comp(a.real + b.real , a.imag + b.imag); } inline friend comp operator-(comp&a , comp&b) { return comp(a.real - b.real , a.imag - b.imag); } inline friend comp operator*(comp&a , comp&b) { return comp(a.real * b.real - a.imag * b.imag , a.imag * b.real + a.real * b.imag); } inline friend void swap(comp&a , comp&b) { comp c = a ; a = b ; b = c; } }A[maxN]; int rev[maxN] , N , Nlen; ll res[maxN]; inline void FFT_clear() { memset(A , 0 , sizeof(comp) * N); } inline void FFT_init() { for (N = 1 , Nlen = 0;N <= lim + lim;N <<= 1 , Nlen ++) ; For (i , 1 , N) rev[i] = (rev[i >> 1] >> 1) | ((i & 1) << Nlen - 1); } const long double ppi = std::acos(-1.0) * 2; inline void FFT(comp*a , int n , int v) { For (i , 0 , n) if (i < rev[i]) swap(a[i] , a[rev[i]]); for (int s = 2;s <= n;s <<= 1) { comp g(cos(ppi / s) , v * sin(ppi / s)); for (int k = 0;k < n;k += s) { comp w(1 , 0); For (j , 0 , s / 2) { comp t = a[k + j + s / 2] * w , u = a[k + j]; a[k + j + s / 2] = u - t , a[k + j] = u + t; w = w * g; } } } if (v == -1) For (i , 0 , n) a[i].real /= n * 4, a[i].imag /= n; } inline void GetConv() { FFT_clear(); rep (i , 0 , lim) A[i] = comp(L[i] + R[i] , L[i] - R[i]); FFT(A , N , 1); For (i , 0 , N) A[i] = A[i] * A[i]; FFT(A , N , -1); for (int i = 0;i <= lim + lim;i += 2) res[i] = (ll) (A[i].real + 0.5); } inline void calc(int p) { int t = a[p] + a[p]; int cur = belong[p]; rep (i , st[cur] , p - 1) if (t >= a[i]) ans += R[t - a[i]]; rep (i , p + 1 , ed[cur]) if (t >= a[i]) { ans += cnt_pre[t - a[i]]; ans += L[t - a[i]]; if (cur == 1) continue; } if (p <= ed[1] || p >= st[tot]) return; ans += res[t]; } void solve() { init_block(); FFT_init(); rep (i , 1 , n) R[a[i]] ++; rep (i , 1 , tot) { memset(cnt_pre , 0 , sizeof(int) * (lim + 1)); rep (j , st[i] , ed[i]) cnt_nxt[a[j]] ++; rep (j , 0 , lim) R[j] -= cnt[i][j]; if (i != 1 && i != tot) GetConv(); rep (j , st[i] , ed[i]) cnt_nxt[a[j]] -- , calc(j) , cnt_pre[a[j]] ++; rep (j , 0 , lim) L[j] += cnt[i][j]; } printf("%lld\n" , ans); } int main() { #ifndef ONLINE_JUDGE freopen("data.txt" , "r" , stdin); freopen("data.out" , "w" , stdout); #endif input(); solve(); return 0; }