Problem
给出一个数组,求各个 (kin[0,N)) 下数组中所有大于 (k) 的值改为 (k) 后的逆序对个数。
Solution
(O(N^2log N)) 解法
Solution
暴力
Code
#include<cstdio>
#include<stack>
#define ls pos<<1
#define rs (pos<<1)+1
#define MID (tree[pos].l+tree[pos].r)>>1
const int MAXN=5e3+5;
struct Tree{int l,r,val;bool clear;};
Tree tree[MAXN*4];int n,a[MAXN];
std::stack<int>sta;
void BuildTree(int l,int r,int pos)
{
tree[pos].l=l;tree[pos].r=r;tree[pos].clear=0;tree[pos].val=0;
if(l==r) return;
int mid=MID;
BuildTree(l,mid,ls);BuildTree(mid+1,r,rs);
}
void DownReload(int pos)
{
if(tree[pos].clear)
{
tree[ls].val=tree[rs].val=0;
tree[ls].clear=tree[rs].clear=1;
tree[pos].clear=0;
}
}
void Update(int target,int pos)
{
if(tree[pos].l==tree[pos].r)
{
tree[pos].val++;
return;
}
DownReload(pos);
int mid=MID;
if(target<=mid) Update(target,ls); else Update(target,rs);
tree[pos].val=tree[ls].val+tree[rs].val;
}
int Query(int l,int r,int pos)
{
if(l>r) return 0;
if(l==tree[pos].l&&r==tree[pos].r)
{
return tree[pos].val;
}
DownReload(pos);
int mid=MID;
if(r<=mid) return Query(l,r,ls); else if(l>mid) return Query(l,r,rs); else {return Query(l,mid,ls)+Query(mid+1,r,rs);}
}
int main()
{
freopen("haircut.in","r",stdin);
freopen("haircut.out","w",stdout);
scanf("%d",&n);
for(int i=1;i<=n;i++)
scanf("%d",&a[i]);
BuildTree(0,n,1);
for(int i=n-1;i>=0;i--)
{
int ans=0;
tree[1].clear=1;
for(int j=1;j<=n;j++)
{
if(a[j]>i) a[j]=i;
// printf("%d %d %d
",i,j,Query(a[j]+1,i,1));
ans+=Query(a[j]+1,i,1);
Update(a[j],1);
}
//printf("%d
",ans);
sta.push(ans);
}
while(!sta.empty()) {printf("%d
",sta.top());sta.pop();}
return 0;
}
(O(N^2)) 解法
Solution
考虑到相邻 (k) 之间变化量等于所有 (k+1) 变到 (k) 导致逆序对的减量之和,因此算这个然后逆着推。
Code
#include<cstdio>
#include<stack>
const int MAXN=1e5+5;
int n,a[MAXN],table[MAXN],sum[MAXN];
std::stack<int>sta;
int main()
{
freopen("haircut.in","r",stdin);
freopen("haircut.out","w",stdout);
scanf("%d",&n);
for(int i=1;i<=n;i++)
scanf("%d",&a[i]);
int rp=0;
for(int i=n;i>=1;i--)
{
for(int j=0;j<a[i];j++)
{
rp+=table[j];
sum[j]+=table[j];
}
table[a[i]]++;
}
for(int i=n-1;i>=0;i--)
{
// printf("[%d] ",sum[i]);
rp-=sum[i];
// printf("%d
",rp);
sta.push(rp);
}
while(!sta.empty()) {printf("%d
",sta.top());sta.pop();}
return 0;
}
(O(Nlog N)) 做法(AC 做法)
Solution
由于 table[j]
每个 (i) 的循环只更新一次,(O(N^2)) 做法中 sum[j]+=table[j];
明显可以被优化,优化后使得每次 table[j]
变化时用乘法加,想起小学老师的“乘法是加法的整合”(也可能记错了),然后加的次数也能用类似逆序对的方法求出来。另外逆序对可以 (O(Nlog N)) 权值线段树求。
- 注意开
long long
- 代码中
lastchange[]
没有用
Code
#include<cstdio>
#include<stack>
const int MAXN=1e5+5;
#define ls pos<<1
#define rs (pos<<1)+1
#define MID (tree[pos].l+tree[pos].r)>>1
int n,a[MAXN];long long table[MAXN],sum[MAXN],lastchange[MAXN],lastchangev[MAXN];
struct Tree{int l,r;long long val;bool clear;};
Tree tree[MAXN*4];
std::stack<long long>sta;
void BuildTree(int l,int r,int pos)
{
tree[pos].l=l;tree[pos].r=r;tree[pos].clear=0;tree[pos].val=0;
if(l==r) return;
int mid=MID;
BuildTree(l,mid,ls);BuildTree(mid+1,r,rs);
}
void DownReload(int pos)
{
if(tree[pos].clear)
{
tree[ls].val=tree[rs].val=0;
tree[ls].clear=tree[rs].clear=1;
tree[pos].clear=0;
}
}
void Update(int target,int pos)
{
if(tree[pos].l==tree[pos].r)
{
tree[pos].val++;
return;
}
DownReload(pos);
int mid=MID;
if(target<=mid) Update(target,ls); else Update(target,rs);
tree[pos].val=tree[ls].val+tree[rs].val;
}
long long Query(int l,int r,int pos)
{
if(l>r) return 0;
if(l==tree[pos].l&&r==tree[pos].r)
{
return tree[pos].val;
}
DownReload(pos);
int mid=MID;
if(r<=mid) return Query(l,r,ls); else if(l>mid) return Query(l,r,rs); else {return Query(l,mid,ls)+Query(mid+1,r,rs);}
}
int main()
{
freopen("haircut.in","r",stdin);
freopen("haircut.out","w",stdout);
scanf("%d",&n);
for(int i=1;i<=n;i++)
scanf("%d",&a[i]);
for(int i=0;i<=n;i++)
lastchange[i]=n+1;
long long rp=0;
BuildTree(0,n,1);
for(int i=n;i>=1;i--)
{
// for(int j=0;j<a[i];j++)
// {
// rp+=table[j];
// sum[j]+=table[j];
// }
rp+=Query(0,a[i]-1,1);
long long v=Query(a[i]+1,n,1);
sum[a[i]]+=table[a[i]]*(v-lastchangev[a[i]]);
Update(a[i],1);
lastchange[a[i]]=i;
lastchangev[a[i]]=v;
table[a[i]]++;
}
for(int i=0;i<n;i++)
{
sum[i]+=table[i]*(Query(i+1,n,1)-lastchangev[i]);
}
for(int i=n-1;i>=0;i--)
{
// printf("[%d: %d] ",i,sum[i]);
rp-=sum[i];
// printf("%d
",rp);
sta.push(rp);
}
while(!sta.empty()) {printf("%lld
",sta.top());sta.pop();}
return 0;
}