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; }
没有人不辛苦,只有人不喊疼