题意:
有一堆数字
1
n
1~n
1 n 的卡片,你需要按照数字
1
n
1~n
1 n 的顺序将他们删去。你只能删去最上面的那张卡片,但你可以将最上面的卡片移动到最底部,也可以将最底部的卡片移动到最上面,问移动次数最少是多少。
很显然,想删去一张卡片,要么把它上面的所有卡片移动到底部,要么从底部将它拿上来。如果将这堆卡片的最上面和底部看作是首尾相连的,那么不管怎么移动,所有卡片的相对位置都是不变的。
用线段树或树状数组,区间查询,单点修改。对于删去第
k
k
k 张卡片,这个时候是第k-1张卡片在顶部。区间查询两张卡片之间剩余的卡片数量,就是将第
k
k
k 张卡片从上或从下(取决于这两张卡片一开始的相对位置)移动到顶部的移动次数
a
,
n
−
k
−
a
a,n-k-a
a,n−k−a 就是另一种移动方式的代价,取较小的那个。然后单点修改删去这张牌。
AC代码:
const int N = 1e5 + 10;
struct node
{
ll a;
int pos;
} s[N];
ll tr[N];
int n;
void add(int x, ll c)
{
int i;
for (i = x; i <= n; i += lowbit(i))
{
tr[i] += c;
}
}
ll sum(int x)
{
int i;
ll res = 0;
for (i = x; i; i -= lowbit(i))
{
res += tr[i];
}
return res;
}
bool cmp(node a, node b)
{
return a.a < b.a;
}
int main()
{
int t;
sd(t);
while (t--)
{
sd(n);
rep(i, 1, n)
{
sld(s[i].a);
s[i].pos = i;
add(i, s[i].a);
}
ll ans = 0;
sort(s + 1, s + 1 + n, cmp);
int pos = s[1].pos;
ans += min(sum(s[1].pos - 1), sum(n) - sum(s[1].pos - 1));
add(s[1].pos, -s[1].a);
rep(i, 2, n)
{
int tmp = s[i].pos;
ll up;
ll down;
if (tmp < pos)
{
up = sum(tmp - 1) + sum(n) - sum(pos - 1);
down = sum(pos) - sum(tmp - 1);
}
else
{
up = sum(tmp - 1) - sum(pos);
down = sum(n) - sum(tmp - 1) + sum(pos);
}
ans += min(up, down);
add(tmp, -s[i].a);
pos = tmp;
}
pld(ans);
}
return 0;
}