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;
}
posted @ 2020-04-10 13:50  酷暑一夏1  阅读(480)  评论(0编辑  收藏  举报