解题报告: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\)我是写不出来。