【ZROI2018暑假刷题营DAY1T2逆序对】
stio oirz
给定一个偶数 N,现在蔡老板得到了一个由[1,N]内的所有偶数构成的排列
b[1...N/2]现在蔡老板又得到了一个数组 a[1....N/2],其中 a[i]=i*2-1蔡老板想知道,对于所有满足 a和b 都是它的子序列的 [1,N]的排列 p,p的逆序对的最小值
输入格式
第一行一个偶数 N 第二行 N/2个偶数,描述 b[1..N/2]输出格式
输出逆序对的最小值样例1
input
6
6 4 2
output
5
样例解释
p=[1,3,6,4,2,5]样例2
见下载文件数据范围
对于 的数据,有 1<=n<=10 对于 30%的数据,有 1<=n<=100 对于 的数据,有1<=n<=1000 对于 的数据,有 1<=n<=5000 对于 100%的数据,有限制
时间限制:1s 空间限制:512MB题解
首先我们看这个部分分,当n<=5000,有50分这个做法就是直接f[i][j]表示奇数序列取前i个数,偶数序列取前j个数,然后就DP直接推着走了,O(n^2) 考虑正解 先直接抛出一个结论:我们首先不管那个奇数序列的顺序,直接每次考虑奇数序列的时候都将奇数的某个数插入到一个最优的位置(产生逆序对个数最小的位置),这样到最后保证奇数序列是单调递增的(满足题意),且答案最优的。 可见这是一个贪心的思路。至于证明,我们在将做法弄完再证,我们大胆猜想有这个结论,考虑之后怎么利用这个取做。 首先我们设定一个数组p[0,1,2....k],(这个k值得是n/2,即每个序列的元素个数),表示在你从小到大枚举要插入的奇数的时候,我们将这个数插入到i*2-1的位置的时候会多产生的逆序对个数为p[i]。我们插入到第i*2+1的位置,我们想像类似插空(因为偶数是固定的位置,我们只能将奇数插在偶数之间),其实一个空内能插入的位置可以不止一个奇数,这个时候只要考虑将大的奇数放在后面就更优(注意我们的结论) 那么这个p数组在我们考虑1这个奇数的时候,p[0]=0,p[1]=1,p[2]=2...p[x]=x....,因为1是最小的数,除非我们将1放在0*2+1的位置,即放在第2个位置前面(偶数序列第一个位置)前不会产生逆序对,其他地方都递增新产生逆序对,这样我们就完成了p数组的初始化工作,而此时我们就将1考虑放在第一个偶数前面。(也容易想到1始终放在第一个位置) 也就是说我们目前已知第2*i-1插入到某个位置后产生的逆序对个数,然后在其中选出最小的值,加入ANS就可以了。然后考虑将2*i-1的p数组再往后推到2*i+1的i数组。这其中跨过了一个i*2的偶数,由于我们是从i*2-1转换到i*2+1的,原本我们将这个奇数(i*2-1)插入到i*2之后的任何位置都会受到(i*2-1,i*2)这个数对贡献的逆序对个数1,而现在我们考虑i*2+1的时候,这个贡献的个数1就没有了,也就是再p[i]一直到p[k]这段数组都整体-1。同理,原本我们在[0*2+1.....i*2-1]这一段插入一个数原本没有逆序对,现在都新增加了(i*2,i*2+1)这一个逆序对,所以我们p[0]一直到p[i-1]这一段数组整体+1。 可以发现,我们整个所有的操作都是区间操作和取整个数组的最小值加入ANS,这个不就是直接套一个裸的线段树么。我们将p数组用线段树 维护一下就可以了。 然后我们想那个结论为什么正确。我们发现整个操作过程中,我们是取的最小值,然后我们操作是使左边前缀一段位置区间p数组整体+1,后缀一段整体区间全部-1,那么就一定有最小值的位置会渐渐向右移动。我们是从小到大一个一个考虑,那么就一定满足到最后满足插入的位置是单调递增的,即满足题意。 据说还有直接利用堆的做法orz 不会 orz#include<iostream> #include<cstdio> #include<algorithm> #include<cstring> #define lowbit(x) ((x)&(-x)) using namespace std; const int maxn = 200005; int n,pos[maxn],a[maxn]; int rt,ls[maxn*2],rs[maxn*2],tot,mi[maxn*2],lazy[maxn*2]; int bit[maxn],md; void add(int x,int d) { for(;x<=n;x+=lowbit(x))bit[x]+=d; } int getsum(int x) { int sum=0; for(;x;x-=lowbit(x)) sum+=bit[x]; return sum; } void putdowm(int x) { mi[ls[x]]+=lazy[x]; lazy[ls[x]]+=lazy[x]; mi[rs[x]]+=lazy[x]; lazy[rs[x]]+=lazy[x]; lazy[x]=0; } void putup(int x) { mi[x]=min(mi[ls[x]],mi[rs[x]]); } int maketree(int l,int r) { int p = ++tot; if(l==r) { mi[p] = l; return p; } int mid = (l+r)>>1; ls[p] = maketree(l,mid); rs[p] = maketree(mid+1,r); putup(p); return p; } void modify(int p,int l,int r,int x,int y,int d) { if(x<=l&&r<=y) { mi[p]+=d; lazy[p]+=d; return; } if(lazy[p]) putdowm(p); int mid = (l+r)>>1; if(x<=mid) modify(ls[p],l,mid,x,y,d); if(y>mid) modify(rs[p],mid+1,r,x,y,d); putup(p); } long long ANS; int main() { scanf("%d",&n); md = n/2; for(int i=1;i<=md;i++) { scanf("%d",&a[i]); pos[a[i]]=i; } for(int i=md;i>=1;i--) ANS+=getsum(a[i]),add(a[i],1); rt=maketree(0,md); for(int i=1;i<=md;i++) { ANS+=mi[rt]; modify(rt,0,md,0,pos[i*2]-1,1); modify(rt,0,md,pos[i*2],md,-1); } printf("%lld",ANS); }