K Integers
Problem Description
You are given a permutation p1,p2,…,pn.
In one move you can swap two adjacent values.
You want to perform a minimum number of moves, such that in the end there will exist a subsegment 1,2,…,k, in other words in the end there should be an integer i, 1≤i≤n−k+1 such that pi=1,pi+1=2,…,pi+k−1=k.
Let f(k)f(k)f(k) be the minimum number of moves that you need to make a subsegment with values 1,2,…,k appear in the permutation.
You need to find f(1)f(1)f(1),f(2)f(2)f(2),…,f(n)f(n)f(n).
Input
The first line of input contains one integer n (1≤n≤200000): the number of elements in the permutation.
The next line of input contains n integers p1,p2,…,pn: given permutation (1≤ pi ≤ n).
Output
In the first line print one integer m: the number of digits in y.
In the next line print m digits b1,b2,…,bm (b1≠0, 0≤bi≤9): digits of y.
Sample Input
5
5 4 3 2 1
Sample Output
0 1 3 6 10
题意
有一个排列,每一步可以交换两个相邻位置的数,现在需要求出对于每个k,使排列存在子串1,2,3…k所需要移动的最少次数。
题解:
对于每个k可以看出答案可以分为两个部分,一个部分是将1,2,3…k移动到相邻的位置,一个部分是将乱序的1,2,3…k调整为升序。
对于前面一部分,可以将小于等于k的数看为1,大于k的看做0,然后求前缀和pi,那么对于所有为0的数,对答案的贡献为(pi,k-pi)。对于k,显然pi<k/2+1,应该加上pi,pi>=k/2+1应该加上k-pi。所以对于每个k,需要求出第k/2+1个元素的位置c对于c之前的,所有前缀和pi求和,对于c之后的,应该求
k−p[c]+k−p[c+1]+...+k−p[n]k-p[c] + k-p[c+1] + ... + k-p[n]k−p[c]+k−p[c+1]+...+k−p[n]
即k∗(n−c+1)−(p[c]+p[c+1]+...+p[n])k*(n-c+1) - (p[c]+p[c+1]+...+p[n])k∗(n−c+1)−(p[c]+p[c+1]+...+p[n])即可。注意这之中还包括为1的数的情况,所以需要减去对1~k,min(i,k-i)的和。
对于第二部分,则可以求逆序数对即可。
#include<cstdio>
#include<iostream>
#include<cstdlib>
#include<cmath>
#include<algorithm>
#include<ctype.h>
#include<cstring>
#include<vector>
#include<queue>
#include<map>
#include<iterator>
#define dbg(x) cout<<#x<<" = "<<x<<endl;
#define INF 0x3f3f3f3f
#define eps 1e-8
using namespace std;
typedef long long LL;
typedef pair<int, int> P;
const int maxn = 200100;
const int mod = 1000000007;
int lz[4*maxn], a[maxn], b[maxn], c[maxn];
LL ans[maxn], p[4*maxn];
void creat(int l, int r, int k);
void pushdown(int k, int l, int r);
int getId(int l, int r, int x, int k);
LL query(int l, int r, int al, int ar, int k);
void Update(int l, int r, int al, int ar, int k);
int main()
{
int n, i, j, k;
scanf("%d", &n);
for(i=1;i<=n;i++)
{
scanf("%d", &a[i]);
b[a[i]] = i;
}
creat(1, n, 1);
//求逆序数对,即后半部分的贡献
for(i=1;i<=n;i++)
{
ans[i] = ans[i-1]+query(1,n,b[i], n, 1);
Update(1, n, b[i], b[i], 1);
//c[i]表示以分界,即c[i]之前的pi较小,c[i]之后的k-pi较小
c[i] = getId(1, n, i/2+1, 1);
}
//求前半部分的贡献
creat(1, n, 1);
for(i=1;i<=n;i++)
{
Update(1, n, b[i], n, 1);
LL sum = query(1, n, 1, c[i]-1, 1) + (LL)i*(n-c[i]+1) - query(1, n, c[i], n, 1);
LL l = (i-1)/2, r=i-1-l;
sum -= (l+1)*l/2 + (r+1)*r/2;
ans[i] += sum;
}
for(i=1;i<=n;i++)
printf("%I64d ", ans[i]);
return 0;
}
void creat(int l, int r, int k)
{
p[k] = 0;
lz[k] = 0;
if(l == r)return;
int mid = (l+r)/2;
creat(l, mid, 2*k);
creat(mid+1, r, 2*k+1);
}
void Update(int l, int r, int al, int ar, int k)
{
p[k] += (ar-al+1);
if(l ==al && ar == r){
lz[k]++;
return ;
}
pushdown(k, l, r);
int mid = (l+r)/2;
if(ar <= mid)Update(l, mid, al, ar, 2*k);
else if(al > mid)Update(mid+1, r, al, ar, 2*k+1);
else Update(l, mid, al, mid, 2*k),
Update(mid+1, r, mid+1, ar, 2*k+1);
}
void pushdown(int k, int l, int r)
{
if(lz[k])
{
int mid = (l+r)/2;
lz[2*k] += lz[k];
lz[2*k+1] += lz[k];
p[2*k] += lz[k] *(mid-l+1);
p[2*k+1] += lz[k] *(r-mid);
lz[k] = 0;
}
}
LL query(int l, int r, int al, int ar, int k)
{
if(al > ar)return 0;
if(l == al && r == ar)
return p[k];
int mid = (l+r)/2;
pushdown(k, l, r);
if(ar <= mid)
return query(l, mid, al, ar, 2*k);
else if(al > mid)
return query(mid+1, r, al, ar, 2*k+1);
else
return query(l, mid, al, mid, 2*k)+
query(mid+1, r, mid+1, ar, 2*k+1);
}
int getId(int l, int r, int x, int k)
{
if(l == r)return l;
int mid = (l+r)/2;
pushdown(k, l, r);
if(x <= p[2*k])return getId(l, mid, x, 2*k);
else return getId(mid+1, r, x-p[2*k], 2*k+1);
}
本文深入探讨了KIntegers问题,这是一个关于排列和最优化移动次数的问题。文章详细解释了解决方案,包括如何通过两部分策略来最小化移动次数,一是调整数值位置,二是修正乱序子序列。通过巧妙的数据结构和算法,如线段树,实现了高效求解。
868

被折叠的 条评论
为什么被折叠?



