表示期末考试终于度过了。。虽然有一科不幸阵亡,不过也算差强人意了0.0~
额,在恢复手感的同时学点什么也得,所以先挑了这个,刚看完刘汝佳大大的书,简单的知道了什么是树状数组之后。开开心心看了这道例题然后就跪了。。完全没找到用什么姿势去用树状数组啊= =|||。
想了半天也没什么结果之后,看了书上给的思想以及网上的题解才懂了这道题的姿势,不过感觉依旧不怎么知道如何辨认出一道树状数组的题以及用它的常用姿势的说TAT。。。
这道题除了树状数组,还需要的就是用个小数学知识。对于n个人中,某个能力值排在第i位、家位置排在第j位的人当裁判而言,如果在他家左侧的能力值比他小的人有low个的话,那么在他家左侧能力值比他大的人就有j-1-low个;而因为他能力排在第i位,那么与此同时我们也知道了他家右侧的能力值比他小的人有i-1-low个。
由此可有公式,某人当裁判共有的情况数为:low*((n-i)-(b[i]-1-low)) + (i-1-low)*(b[i]-1-low)
以下是这道题的代码(需要注意的是最终的结果由于比较大,别忘了开成long long型):
#include#include #include #include using namespace std;int a[20010],b[20010],num[20010],n;//数组a记录能力值,b记录位置,num记录某按能力排序后当过裁判的人的位置int lowbit(int x){ return x&(-x);}int sum(int x){ int ret = 0; while(x > 0){ ret += num[x]; x -= lowbit(x); } return ret;}void add(int x,int d){ while(x <= n){ num[x] += d; x += lowbit(x); }}int cmp(int x,int y){ return a[x] <= a[y];}int main(){ int t,low; long long ans; scanf("%d",&t); while(t--){ ans = 0; memset(num, 0, sizeof(num)); scanf("%d",&n); for(int i = 1; i <= n; i++){ scanf("%d", &a[i]); b[i] = i; } sort(b+1, b+n+1, cmp); add(b[1],1);//当过裁判便给这个人的位置+1,记为用过 for(int i = 2; i < n; i++){ low = sum(b[i]); ans += low*((n-i)-(b[i]-1-low)) + (i-1-low)*(b[i]-1-low); add(b[i],1); } printf("%lld\n",ans); } return 0;}