USACO USOpen 2020 Gold T1 Hair Cut 题解
Problem
给出一个数组,求各个 \(k\in[0,N)\) 下数组中所有大于 \(k\) 的值改为 \(k\) 后的逆序对个数。
Solution
\(O(N^2\log 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\n",i,j,Query(a[j]+1,i,1));
ans+=Query(a[j]+1,i,1);
Update(a[j],1);
}
//printf("%d\n",ans);
sta.push(ans);
}
while(!sta.empty()) {printf("%d\n",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\n",rp);
sta.push(rp);
}
while(!sta.empty()) {printf("%d\n",sta.top());sta.pop();}
return 0;
}
\(O(N\log N)\) 做法(AC 做法)
Solution
由于 table[j]
每个 \(i\) 的循环只更新一次,\(O(N^2)\) 做法中 sum[j]+=table[j];
明显可以被优化,优化后使得每次 table[j]
变化时用乘法加,想起小学老师的“乘法是加法的整合”(也可能记错了),然后加的次数也能用类似逆序对的方法求出来。另外逆序对可以 \(O(N\log 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\n",rp);
sta.push(rp);
}
while(!sta.empty()) {printf("%lld\n",sta.top());sta.pop();}
return 0;
}
无特别声明的情况下,本文为原创文章,允许转载,采用知识共享署名-非商业性使用-相同方式共享 4.0 国际许可协议进行许可。
在声明禁止转载的情况下,请勿转载;若本文章为转载的文章,版权归原作者所有。
如果您觉得本文写得好,请点击下方的推荐按钮~若您有任何建议和指正,请在下方留言,对于您的指正将不胜感激。
在声明禁止转载的情况下,请勿转载;若本文章为转载的文章,版权归原作者所有。
如果您觉得本文写得好,请点击下方的推荐按钮~若您有任何建议和指正,请在下方留言,对于您的指正将不胜感激。