……

解题报告:luogu P6186/NOI ONLINE T2

题目链接\(1\)P6186 [NOI Online 提高组]冒泡排序
感觉\(T2\)最水了,可是还是没想出来。
大概要先会逆序对,有个板子题,然而我考试之前还没做过。
题目链接\(2\):P1908 逆序对
树状数组还要离散化,直接归并好了。
注意边界,否则\(T\)掉两行泪啊。

\(Code\):

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
using namespace std;
typedef long long ll;
const int MAXN=500005;
int n,m,a[MAXN],b[MAXN],rt[MAXN];
ll ans=0;
inline int read()
{
	int x=0,w=1;
	char c=getchar();
	while(c<'0'||c>'9')
	{
		if(c=='-') w=-1;
		c=getchar();
	}
	while(c>='0'&&c<='9')
	{
		x=(x<<1)+(x<<3)+(c^'0');
		c=getchar();
	}
	return x*w;
}
inline void print(int x)
{
	if(x<0) x=-x,putchar('-');
	if(x>=10) print(x/10);
	putchar(x%10^'0');
}
inline void qsort(int l,int r)
{
	if(l==r) return;
	int mid=(l+r)>>1;
	qsort(l,mid),qsort(mid+1,r);
	int i=l,j=mid+1,c=l-1;
	while(i<=mid&&j<=r)
	{
		if(a[i]<=a[j]) rt[++c]=a[i++];
		else rt[++c]=a[j++],ans+=(ll)(mid-i+1);
	}
	while(i<=mid) rt[++c]=a[i++];
	while(j<=r) rt[++c]=a[j++];
	for(register int i=l;i<=r;i++) a[i]=rt[i]; 
}
int main()
{
	n=read();
	for(register int i=1;i<=n;i++) a[i]=read();
	qsort(1,n);
	printf("%lld",ans);
	return 0;
} 

大概还要注意开\(long\;long\)
好了,进入正题。
然后我们发现一个规律,每一轮冒泡排序使得每个数前面比他大的数减一(当然是在存在的前提下),对于每个数前面比他大的数,在归并时求出即可。
注意是\(1\)\(n\)的排列,这样就不需要离散化了,可是我还是码了。
用一棵线段树来维护下即可。
注意交换时,分两种情况来讨论。
*,东西太多了,还是看代码吧,我把没用的都删了,否则就有\(5KB\)了,\(qwq\).

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
using namespace std;
typedef long long ll;
const int MAXN=200005;
const ll inf=2147483647;
ll n,m,c[MAXN],num[MAXN];
int b[MAXN];
inline ll read()
{
	ll x=0,w=1;
	char c=getchar();
	while(c<'0'||c>'9')
	{
		if(c=='-') w=-1;
		c=getchar();
	}
	while(c>='0'&&c<='9')
	{
		x=(x<<1)+(x<<3)+(c^'0');
		c=getchar();
	}
	return x*w;
}
struct node
{
	int id;
	ll val;
}at[MAXN],rt[MAXN],cnt[MAXN];
bool cmp(node n,node m){return n.val<m.val;}
inline void qsort(int l,int r)
{
	if(l>=r) return;
	int mid=(l+r)>>1;
	qsort(l,mid),qsort(mid+1,r);
	int i=l,j=mid+1,c=l-1;
	while(i<=mid&&j<=r)
	{
		if(at[i].val<=at[j].val) rt[++c]=at[i++];
		else rt[++c]=at[j],cnt[at[j++].id].val+=(ll)(mid-i+1);
	}
	while(i<=mid) rt[++c]=at[i++];
	while(j<=r) rt[++c]=at[j++];
	for(register int i=l;i<=r;i++) at[i]=rt[i]; 
}
void pro()
{
	sort(cnt+1,cnt+n+1,cmp);
	//这里的b数组代表原数组的逆序对数在所有的数中排老几; 
	//这里的num数组代表线段树中第几个数在原数组中的位置。 
	for(int i=1;i<=n;i++) b[cnt[i].id]=i,num[i]=cnt[i].id;
	return;
}
struct tree
{
	ll minn,maxn,sum;
	ll l,r;
	tree(){maxn=sum=0,minn=inf;}
}a[MAXN<<2];
void update(int k)
{
	a[k].sum=a[k<<1].sum+a[k<<1|1].sum;
	a[k].maxn=max(a[k<<1].maxn,a[k<<1|1].maxn);
	a[k].minn=min(a[k<<1].minn,a[k<<1|1].minn);
	return;
}
void build(int k,ll l,ll r)
{
	ll mid=(l+r)>>1;
	a[k].l=l,a[k].r=r;
	if(a[k].l==a[k].r)
	{
		a[k].maxn=a[k].minn=a[k].sum=(ll)cnt[l].val;
		return;
	}
	build(k<<1,l,mid),build(k<<1|1,mid+1,r);
	update(k);
}
//查找小x于等于的所有位置,返回右端点。 
int lit_first(int k,ll x)
{
	if(a[k].l==a[k].r) return a[k].r;
	if(x>=a[k<<1|1].minn) return lit_first(k<<1|1,x);
	else return lit_first(k<<1,x);
}
//查询比大于等于x的第一个数,返回这个编码 
int lit_second(int k,ll x)
{
	if(a[k].l==a[k].r) return a[k].l;
	if(x<=a[k<<1].maxn) return lit_second(k<<1,x);
	else return lit_second(k<<1|1,x);
}
//naive的区间和  
ll query(int k,ll l,ll r)
{
	ll mid=(a[k].l+a[k].r)>>1;
	if(a[k].l==l&&a[k].r==r) return a[k].sum;
	if(r<=mid) return query(k<<1,l,r);
	else if(l>=mid+1) return query(k<<1|1,l,r);
	else return query(k<<1,l,mid)+query(k<<1|1,mid+1,r);	
} 
//单点修改,你值得拥有 
//把x点搞成y 
void turn(int k,ll x,ll y)
{
	ll mid=(a[k].l+a[k].r)>>1;
	if(a[k].l==a[k].r)
	{
		a[k].sum=a[k].maxn=a[k].minn=(ll)y;
		return;	
	}
	if(x<=mid) turn(k<<1,x,y);
	else turn(k<<1|1,x,y);
	update(k);
} 
ll flag;
int k;
int main()
{
	//freopen("data.in","r",stdin);
	//freopen("baoli.out","w",stdout);
	scanf("%lld%lld",&n,&m);
	for(register int i=1;i<=n;i++) at[i].val=read(),c[i]=at[i].val,at[i].id=i;
	qsort(1,n);
	for(int i=1;i<=n;i++) cnt[i].id=i;
	pro();

	build(1,1,n);
	for(int i=1;i<=m;i++)
	{
		scanf("%lld%d",&flag,&k);
		if(flag==1)
		{
			if(c[k]<c[k+1])
			{
				swap(c[k],c[k+1]);
				swap(b[k],b[k+1]);
				num[b[k]]=k,num[b[k+1]]=k+1;
				k+=1; 
				ll now=query(1,b[k],b[k]);
				int rr=lit_first(1,now);
				if(b[k]==rr)
				{
					turn(1,b[k],now+1);
					continue;
				}
				swap(b[k],b[num[rr]]);
				swap(num[rr],num[b[num[rr]]]);
				turn(1,b[k],now+1); 
			}
			else if(c[k]>c[k+1])
			{
				swap(c[k],c[k+1]);
				swap(b[k],b[k+1]);
				num[b[k]]=k,num[b[k+1]]=k+1;
				ll now=query(1,b[k],b[k]);
				int rr=lit_second(1,now); 
				if(b[k]==rr)
				{
					turn(1,b[k],now-1);
					continue;
				}
				swap(b[k],b[num[rr]]);
				swap(num[rr],num[b[num[rr]]]);
				turn(1,b[k],now-1); 
			}
		}
		else if(flag==2)
		{
			if(k>=a[1].maxn)
			{
				printf("0\n");
				continue;
			}
			else if(k==0)
			{
				printf("%lld\n",a[1].sum);
				continue;
			}
			ll rr=lit_first(1,k);
			printf("%lld\n",query(1,rr+1,n)-(n-rr)*k);
	 	}
	}
	return 0;
} 

每个函数都是有用的,反正给我\(3.5h\)我是写不出来。

posted @ 2020-03-10 10:26  童话镇里的星河  阅读(155)  评论(0编辑  收藏  举报