题解 CF538F 【A Heap of Heaps】
思路:
\(\quad\)因为此题的答案就像树上的逆序对 \((x,y)\) 个数, \(y\) 比 \(x\) 的编号大但数值小,这不就是树上的逆序对吗?但是不用考虑所有编号大于它的,只需要考虑它的 \(k\) 个儿子(也可能没有 \(k\) 个儿子),儿子的子树不算。
\(\quad\)考虑用树状数组维护,记录每一个点的编号和大小,因为只有小于这个点的才有可能对这个点产生贡献,按大小从小到大排序,先每次计算贡献,再将它插入它的编号的位置即可,计算和插入的顺序不重要,注意要判断节点 \(i\) 是否为叶子节点,是就直接停止,因为之后节点 \(i\) 都会是叶子节点,对于 \(k\) 叉堆来说, \(k\) 越大,叶子节点越多,这个剪枝很必要。
\(\quad\)另外英文题面中提供了对于 \(k\) 叉堆中编号为 \(x\) 的 \(k\) 个儿子编号由 \(k\times (x-1)+2\) 至 \(k\times x+1\) (也可能没有 \(k\) 个儿子)
\(\quad\)想看的可以看看中文题面。
#include<iostream>
#include<cstdio>
#include<cmath>
#include<string>
#include<algorithm>
#include<map>
#include<queue>
#include<stack>
#include<cstring>
using namespace std;
#define re register int
#define int long long
#define LL long long
#define il inline
#define lowbit(x) x&(-x)
#define next nee
#define inf 1e18
il int read()
{
int x=0,f=1;char ch=getchar();
while(!isdigit(ch)&&ch!='-')ch=getchar();
if(ch=='-')f=-1,ch=getchar();
while(isdigit(ch))x=(x<<1)+(x<<3)+ch-'0',ch=getchar();
return x*f;
}
il void print(int x)
{
if(x<0)putchar('-'),x=-x;
if(x/10)print(x/10);
putchar(x%10+'0');
}
const int N=2e5+5;
int n,m,b[N],c[N];
struct node{int num,size;}a[N];
il bool cmp(node a1,node a2){return a1.size==a2.size?a1.num<a2.num:a1.size<a2.size;}
il void update(int x,int y){while(x<=n){c[x]+=y;x+=lowbit(x);}}
il int sum(int x){int res=0;while(x){res+=c[x];x-=lowbit(x);}return res;}
signed main()
{
n=read();
for(re i=1;i<=n;i++){a[i].num=i;a[i].size=read();}
sort(a+1,a+n+1,cmp);
update(a[1].num,1);
for(re i=2;i<=n;i++)
{
for(re j=1;j<n&&j*(a[i].num-1)+2<=n;j++)//限制中的剪枝
{
int suml=sum(j*(a[i].num-1)+1),sumr=sum(min(j*a[i].num+1,n));//编号最小的儿子-1和编号最大的儿子(不可超过n,因为树状数组最大到n)
b[j]+=(sumr-suml);
}
update(a[i].num,1);//最后更新这个点
}
for(re i=1;i<n;i++)
print(b[i]),putchar(' ');
return 0;
}