UCF Local Programming Contest 2017 I题 Rotating Cards(贪心+树状数组)

这道题首先我们要发现的一个性质是,不过从是从后面取上来,还是从前面放到后面,当我们把想要的数字输出之后,序列是不变的,不信可以试试

这个是关键点,因此,我们只要考虑哪种情况小就取哪种情况就行了,不用考虑顺序问题。

之后还有一个问题是,如果快速求取花费值,我们进一步发现,这里的求值,永远是一段一段求的,

比如 3 5 1 4 2,当我们取1的话,要不是后面一段要不是前面一段,对于区间求和,自然想到树状数组。但是当我们取完1后,序列发生改变,也就是顺序变化了,不能按之前的区间求和

变成 4 2 3 5,所以我们要进一步发现性质,存一个pos值表示上次删的是哪个数,之后把当前要删的数的原位置和他进行比较,进行判断。

那么比完之后该怎么求呢,这就需要大家自己推了,也可以看我的代码理解,提示的一点是,我们可以想象,当前值的位置在之前被删的数的前面,那么当前值和他前面的数都要移到后面去

之后可以自己画图思考一下如何计算到底要求的是哪段和

#include<iostream>
#include<cstring>
#include<cstdio>
#include<map>
#include<algorithm>
#include<queue>
#include<set>
#define ull unsigned long long
using namespace std;
typedef long long ll;
typedef pair<int,int> pll;
const int N=1e5+10;
struct node{
    ll a;
    int pos;
}s[N];
ll tr[N];
int n;
int lowbit(int x){
    return x&-x;
}
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;
    cin>>t;
    while(t--){
        cin>>n;
        int i;
        for(i=1;i<=n;i++){
            scanf("%lld",&s[i].a);
            s[i].pos=i;
            add(i,s[i].a);
        }
        ll res=0;
        sort(s+1,s+1+n,cmp);
        int pos=s[1].pos;
        res+=min(sum(s[1].pos-1),sum(n)-sum(s[1].pos-1));
        add(s[1].pos,-s[1].a);
        for(i=2;i<=n;i++){
            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);
            }
            res+=min(up,down);
            add(tmp,-s[i].a);
            pos=tmp;
        }
        cout<<res<<endl;
    }
    return 0;
}
View Code
原文地址:https://www.cnblogs.com/ctyakwf/p/12643021.html