您的位置:首页 > 其它

牛客练习赛7 E 题 珂朵莉的数列 【树状数组 + 思维】

2018-01-17 01:26 369 查看
传送门

//对于一个数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);

}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: