1. 程式人生 > >牛客練習賽7 E 題 珂朵莉的數列 【樹狀陣列 + 思維】

牛客練習賽7 E 題 珂朵莉的數列 【樹狀陣列 + 思維】

傳送門
//對於一個數n, 它有n*(n+1)/2個子區間, 問這些子區間的逆序對數之和.

//那麼這個區間問題一般都是計算貢獻問題, 所以先分析問題如果有 i < j 且 a[i] > a[j] 那麼區間1<= l <=i, j <= r <= n, 就是包含了這個逆序對, 也就是有i*(n-j+1)個區間. 首先數字太大需要離散化, 然後我們更新的是每一個數的位置, add(pos, i); 並且從後往前掃, 為什麼了? 也就是我們假設第4個位置的數a4和前面的三個數(a1, a2, a3)都是逆序對關係, 那麼ans += (pos(a1) + pos(a2) + pos(a3)) * (n - 4 + 1) , 有多少個進行類推, 也就是包含(a1, a4)這個逆序對的是(1 - pos(a1))x(n-4+1), 所以後面進行類推, 所以要進行推位置進行, 然後詢問位置和, 也就是求字尾來求位置和. (因為字首處理不了, 不行可以試試~ 反正我試了下, 沒寫出來, 所以用字尾和的方法可以很好地求出ans和位置和~~~記住!!!)

注意一點的是: 最後的答案可以到達O(n^4)方級別, 所以long long 也是存不下的, 那麼就有一個小技巧對於>1e18 && < 1e36的數我們就可以有兩位來存, 相當於1e18進位制, 那麼最後輸出的特判下就行啦~

AC Code

const int maxn = 1e6+5;
ll c[maxn], a[maxn];
vector<int>ve;
ll n;
void add(int x, int s){ for(;x<=n;x+=x&-x) c[x]+=s;}
ll getsum(int x){ ll res=0;for
(;x;x-=x&-x) res+=c[x];return res;} int getid(int x) { return lower_bound(ve.begin() , ve.end(), x) - ve.begin() + 1; } void solve() { cin >> n; for (int i = 1 ; i <= n ; i ++) { cin >> a[i]; ve.pb(a[i]); } sort(ve.begin(), ve.end()); ve.erase(unique(ve.begin(),ve.end()), ve.end()); ll ans1 = 0
, ans = 0; ll mm = 1e18; for (int i = 1 ; i <= n ; i ++) { int pos = getid(a[i]); ans += (getsum(n) - getsum(pos))*(n-i+1); // 如前面分析的那樣. if (ans > mm) { ans1 += ans/mm; ans %= mm; } add(pos, i); } if(ans1) printf("%lld%018lld\n", ans1, ans); //爆ll的儲存技巧~ else printf("%lld\n", ans); }