兴致勃勃的弱鸡想去学主席树了!
可惜突然发现动态开点线段树不会了。。。。
发挥大胆猜想,不用求证的精神:你们都会线段树
众所周知:线段树空间复杂度是O4*n,这就不用证,因为我菜
那当n很大,操作正常时,如果像正常的线段树一样(记左儿子为n+n,右儿子为n+n+1),再开四倍n会炸内存
怎么办呢?
想一想:时间复杂度与原来几乎差不多 mlg n,与原来差距只有没几倍的常数而已
但是为什么每次操作时间才lg n?
因为每次只会经过·lg n个结点,也就是总共最多经过m lg n个结点
再m lg n远远小于4n的情况下,再开4n的结点不是很浪费吗?
不一定每个点都有用
所以就可以动态开点啦!
记左儿子lc,右儿子rc就够了
例题:Luogu P1908 逆序对
https://www.luogu.org/problemnew/show/P1908
就是求个逆序对
可以离散化后树状数组,线段树
但是可以用动态开点线段树!
按上文的说法,nlgn的空间,开3个数组,必炸内存,但是我只开了1e7的数组还是过了,
哈哈哈哈
#include
#define ll long long
using namespace std;
const int N=1e7+7; //就这么点
int n,r;
ll ans;
struct A
{
int cnt,c[N],lc[N],rc[N];
int sum(int p,int l,int r,int x,int y)
{
if(!p) return 0;
if(l==x&&r==y) return c[p];
int mid=l+r>>1;
if(y<=mid) return sum(lc[p],l,mid,x,y);
if(x>mid) return sum(rc[p],mid+1,r,x,y);
return sum(lc[p],l,mid,x,mid)+sum(rc[p],mid+1,r,mid+1,y);
}
void add(int &p,int l,int r,int x,int k)
{
if(!p) p=++cnt;
if(l==r)
{
c[p]+=k; return;
}
int mid=l+r>>1;
if(x<=mid) add(lc[p],l,mid,x,k);
else add(rc[p],mid+1,r,x,k);
c[p]=c[lc[p]]+c[rc[p]];
}
}tree;
int main()
{
scanf("%d",&n);
for(int i=1;i<=n;i++)
{
int x; scanf("%d",&x);
ans+=tree.sum(r,1,1e9,x+1,1e9);
tree.add(r,1,1e9,x,1);
}
printf("%lld",ans);
return 0;
}