题解:P15576 [USACO26FEB] Good Cyclic Shifts G
_zuoqingyuan · · 题解
提供一个抛弃大脑的做法。呈现蒟蒻的所有思路。
::::info[转化]
原题操作等价于给排列排序,根据冒泡排序的原理,最小操作次数等价于排列逆序对数。
::::
然后就有
::::info[如何快速计算所有循环位移排列的逆序对?]
我们求出没有位移的排列的逆序对,可以用树状数组
考虑在循环位移
主要变化是我们将
消失的贡献:
产生的贡献:即
然后直接递推转移,这部分是
但是我们还需要计算另外一个部分才能得到答案。
::::info[如何快速计算所有循环位移排列的
考虑一些暴力一点的做法。先维护所有
循环位移
全局加一可以通过偏移量快速维护,删除的
然后是查询全局绝对值,根据当前的偏移量
把所有信息放在值域上维护,记录每个值域区间里数的加和和个数和,然后拿线段树维护即可。
时间复杂度为
其实看整个题,都是一些很套路的东西,只要套路题做多了就能很自然的想到。
最终时间复杂度为
::::info[Code]
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=5e5+10;
int n,p[N],c[N];
vector<int>ans;
void add(int x,int y){
for(;x<=n;x+=(x&-x))c[x]+=y;
return;
}
int ask(int x,int cnt=0){
for(;x>0;x-=(x&-x))cnt+=c[x];
return cnt;
}
ll sum[N<<2],val[N<<2],f[N],g[N];
#define ls (p<<1)
#define rs (p<<1|1)
void pushup(int p){
sum[p]=sum[ls]+sum[rs];
val[p]=val[ls]+val[rs];
}
void upd(int p,int l,int r,int x,int v){
if(l==r){
sum[p]+=v*x,val[p]+=v;
}else{
int mid=(l+r)>>1;
if(x<=mid)upd(ls,l,mid,x,v);
if(x>mid)upd(rs,mid+1,r,x,v);
pushup(p);
}
return;
}
ll ask(int p,int l,int r,int L,int R){
if(L<=l&&r<=R)return sum[p];
int mid=(l+r)>>1;ll cnt=0;
if(L<=mid)cnt+=ask(ls,l,mid,L,R);
if(R>mid)cnt+=ask(rs,mid+1,r,L,R);
return cnt;
}
ll count(int p,int l,int r,int L,int R){
if(L<=l&&r<=R)return val[p];
int mid=(l+r)>>1;ll cnt=0;
if(L<=mid)cnt+=count(ls,l,mid,L,R);
if(R>mid)cnt+=count(rs,mid+1,r,L,R);
return cnt;
}
void build(int p,int l,int r){
sum[p]=val[p]=0;
if(l==r)return;
int mid=(l+r)>>1;
build(ls,l,mid);
build(rs,mid+1,r);
return;
}
#undef ls
#undef rs
void work(){
scanf("%d",&n);
build(1,-n,n);
for(int i=1;i<=n;i++){
scanf("%d",p+i);
f[i]=g[i]=0;
}
for(int i=1;i<=n;i++){
f[1]+=i-1-ask(p[i]);
add(p[i],1);
}
for(int i=1;i<=n;i++)c[i]=0;
for(int i=1;i<n;i++){
f[i+1]=f[i]+n-2*p[i]+1;
}
for(int i=1;i<=n;i++){
upd(1,-n,n,p[i]-i,1);
g[1]+=abs(p[i]-i);
}
for(int i=1,k;i<n;i++){
upd(1,-n,n,p[i]-i,-1);k=i;
upd(1,-n,n,p[i]-n-k,1);
ll cnt0=count(1,-n,n,-n,-k);
ll sum0=ask(1,-n,n,-n,-k);
ll cnt1=count(1,-n,n,-k+1,n);
ll sum1=ask(1,-n,n,-k+1,n);
g[i+1]=-sum0-cnt0*k+cnt1*k+sum1;
}
ans.clear();
for(int i=1;i<=n;i++){
g[i]/=2;
if(f[i]<=g[i])ans.push_back((n-i+1)%n);
}
sort(ans.begin(),ans.end());
printf("%d\n",ans.size());
for(int i=0;i<ans.size();i++){
if(i)printf(" ");
printf("%d",ans[i]);
}
printf("\n");
return;
}
int main(){
//freopen("Shifts.in","r",stdin);
//freopen("Shifts.out","w",stdout);
int T;scanf("%d",&T);
while(T--)work();
return 0;
}
::::
如有错误,请指出。