又是一道没有来源的题
题目描述
对于 15% 的数据,满足
n
≤
300
n\le 300
n≤300;
对于 50% 的数据,满足
n
≤
3000
n\le 3000
n≤3000;
对于另外 35% 的数据,满足
a
a
a 是个排列。
题解
ZXY yydBUS!!
首先考虑是排列的情况怎么做:容易发现每次交换操作恰好减少一个正序对,所以swap的总次数等于正序对个数。
除了swap次数,这题还要求vs的和。我们考虑哪些位置会轮空(即vs=0),发现如果存在
j
≤
i
<
k
j\le i<k
j≤i<k 使得
a
j
<
a
k
a_j<a_k
aj<ak,那么
i
i
i 处不可能轮空,反之一定轮空。
所以我们只需用树状数组维护第一部分,用单调栈维护第二部分即可,配合纯暴力可以拿50分。
但是一旦出现了相同的数,上面的结论就会出问题。
对于swap次数,如果我们规定只算每种元素第一个位置后面大于它的数的个数为正序对,那么结论仍然是成立的。
对于vs的和,上面的结论就完全扩展不过来了。
我们考虑每次在序列末尾加入一个数的时候,操作会发生什么变化。对于前面的数组,肯定是已经降序排序了的。假设我们加入的这个数为 x x x,找到第一个 < x <x <x 的位置 i i i,那么 i i i 处必定会被替换为 x x x;由于 i i i 处的元素必定是 i ∼ n i\sim n i∼n 的最大值,所以它会跑到数组末尾去,然后又变成了一个子问题,找第一个 < a i <a_i <ai 的位置…
我们发现,每次插入一个元素 x x x,相当于找到了一条极长下降子序列,然后把 x x x 放最开头,其它位置顺次往后挪。这个极长下降子序列其实就是 i i i 后面的每段相同元素的第一个位置,所以这个子序列往后挪其实等价于 i ∼ n i\sim n i∼n 整体往后挪。这个时候考虑轮空的位置,我们发现这个下降子序列上的位置必定不轮空,并且一个位置只会从轮空变为不轮空。
接下来只需要考虑用数据结构优化这个过程。我们可以用set或者map存每一个轮空并且为相同元素段段首的位置,那么我们每加入一个元素时,枚举到的位置都会打上标记,要么挪向下一个没打标记的位置,要么删除,复杂度是均摊 O ( log n ) O(\log n) O(logn) 的。由于插入一个 x x x 会让一段后缀的元素整体往后挪,所以我们用一个树状数组维护rank即可。
代码
由于map里第二维记录了rank,省去了再查一遍rank的时间,所以这是目前最快的一发
#include<bits/stdc++.h>//JZM yyds!!
#define ll long long
#define uns unsigned
#define IF (it->first)
#define IS (it->second)
#define END putchar('\n')
using namespace std;
const int MAXN=200005;
const ll INF=1e18;
inline ll read(){
ll x=0;bool f=1;char s=getchar();
while((s<'0'||s>'9')&&s>0){if(s=='-')f^=1;s=getchar();}
while(s>='0'&&s<='9')x=(x<<1)+(x<<3)+(s^48),s=getchar();
return f?x:-x;
}
int ptf[50],lpt;
inline void print(ll x,char c='\n'){
if(x<0)putchar('-'),x=-x;
ptf[lpt=1]=x%10;
while(x>9)x/=10,ptf[++lpt]=x%10;
while(lpt)putchar(ptf[lpt--]^48);
if(c>0)putchar(c);
}
inline ll lowbit(ll x){return x&-x;}
int n,a[MAXN],f[MAXN],g[MAXN],dl[MAXN],le;
bool b[MAXN],vis[MAXN];
inline void add(int x,int*F){
for(;x<=n;x+=lowbit(x))F[x]++;
}
inline int sum(int x,int*F){
int res=0;
for(;x>0;x^=lowbit(x))res+=F[x];
return res;
}
map<int,int>mp;
signed main()
{
freopen("function.in","r",stdin);
freopen("function.out","w",stdout);
for(int CASE=read();CASE--;){
n=read();
for(int i=1;i<=n;i++)a[i]=read(),b[i]=vis[i]=0,f[i]=g[i]=0;
ll ans=0;
mp.clear();
for(int i=1;i<=n;i++){
ans+=sum(a[i]-1,f),le=0,add(a[i],g);
if(!vis[a[i]])add(a[i],f),vis[a[i]]=1;
for(auto it=mp.begin();it!=mp.end();it++){
if(IF>=a[i])break;
ans++,b[IS]=1,IS++;
if(b[IS])dl[++le]=IF;
}while(le)mp.erase(dl[le--]);
int x=i-sum(a[i]-1,g);
if(x==i){if(!mp[a[i]])mp[a[i]]=x;}
else ans+=(!b[x]),b[x]=1;
print(ans,i<n?' ':'\n');
}
}
return 0;
}