貪心考慮,考試時想錯了,我想的是移動最大值,枚舉最大值位置,而後把左面和右面逆序對拼起來,node
固然,處理不了有多個最大值狀況,因而我就想着打個部分分吧c++
然而對拍仍是掛,ide
枚舉最大值位置是不對的函數
考慮一種狀況a,b,c,d,e,f,gui
其中d最大可能出現a移動到d右面更優,b,c留在左面,我只移動最大值的話默認a,b,c都在左面spa
附贈一組點4 5 2 3 1 6 7 最優決策是6步,方案是2 4 5 6 7 3 1 .3d
出現了我說的這種狀況code
考試時打這個題花費時間仍是過於多了blog
正解是貪心移動最小值,這樣是保證正確的,只移動最大值至關與限制了一些狀況出現get
而後最小值考慮了全部狀況且必定正確
#include<bits/stdc++.h> using namespace std; #define ll long long #define A 111111 ll p[A],a[A],b[A],c1[A],c2[A]; ll n,ans=0; vector <ll> v[A]; //正 void add1(ll x,ll d){for(ll i=x;i!=0&&i<=100000;i+=i&-i) c1[i]+=d;} ll ask1(ll x,ll ans=0){for(ll i=x;i>=1;i-=i&-i) ans+=c1[i];return ans;} //逆 void add2(ll x,ll d){for(ll i=x;i>=1;i-=i&-i) c2[i]+=d;} ll ask2(ll x,ll ans=0){for(ll i=x;i!=0&&i<=100000;i+=i&-i) ans+=c2[i];return ans;} void sol(){ for(ll i=1;i<=n;i++){ add1(i,1);add2(i,1); v[a[i]].push_back(i); } for(ll i=1;i<=100000;i++){ if(v[i].size()){ for(ll fr=0,ba=v[i].size()-1;fr<=ba;){ ll x=v[i][fr],y=v[i][ba]; ll l=ask1(x-1),r=ask2(y+1); // printf("l=%lld r=%lld\n",l,r); if(l<r){ ans+=l; add1(x,-1); add2(x,-1); fr++; } else { ans+=r; add1(y,-1); add2(y,-1); ba--; } } } } } int main(){ freopen("time.in","r",stdin); freopen("time.out","w",stdout); scanf("%lld",&n); for(ll i=1;i<=n;i++) scanf("%lld",&a[i]),p[i]=i; sol(); printf("%lld\n",ans); }
作過一個相似的題(老司機的狂歡),也是求字典序最小,也是第一問特別簡單,而後根據第一問推第二問
但實在沒想到能夠每一位上二分求字典序最小
推單調性
若是upper_bound有值具備單調性,當前選的值越大,之後高的可能性就越小
若是upper_bound沒值具備單調性,當前選的值越大,之後得分高的可能越小
可是函數是分段的
能夠分兩次二分
這樣每次set暴力check複雜度$n^2*{log}^2$
如今思考如何check
能夠用權值線段樹維護
維護A的沒用上的牌
維護B的沒用上的牌
向上合併時右子樹每一張A均可以於左子樹中B匹配
代碼大體是這樣
void up(ll x){ ll de=min(tr[x<<1|1].L,tr[x<<1].R); tr[x].s=tr[x<<1].s+tr[x<<1|1].s+de; tr[x].L=tr[x<<1].L+tr[x<<1|1].L-de; tr[x].R=tr[x<<1].R+tr[x<<1|1].R-de; }
#include<bits/stdc++.h> using namespace std; #define ll int #define A 1010100 #define maxx 100000 ll read(){ll x;scanf("%d",&x);return x;} ll tot,n; ll b[A],a[A]; multiset<ll> st; multiset<ll> ::iterator it; struct node{ ll l,r,L,R,s; //L:A的牌 //R:B的牌 //s得分 }tr[A]; void built(ll x,ll l,ll r){ tr[x].l=l,tr[x].r=r; if(l==r){ return ; } ll mid=(l+r)>>1; built(x<<1,l,mid); built(x<<1|1,mid+1,r); } void up(ll x){ ll de=min(tr[x<<1|1].L,tr[x<<1].R); tr[x].s=tr[x<<1].s+tr[x<<1|1].s+de; tr[x].L=tr[x<<1].L+tr[x<<1|1].L-de; tr[x].R=tr[x<<1].R+tr[x<<1|1].R-de; } void insert(ll x,ll pla,ll vala,ll valb){ if(tr[x].l==tr[x].r){ tr[x].L+=vala; tr[x].R+=valb; return ; } ll mid=(tr[x].l+tr[x].r)>>1; if(mid>=pla) insert(x<<1,pla,vala,valb); else insert(x<<1|1,pla,vala,valb); up(x); } void getans(ll now){ insert(1,b[now],0,-1); ll l=b[now]+1,r=*st.rbegin(),ans=0; while(l<=r){ ll mid=(l+r)>>1; insert(1,mid,-1,0); if(tr[1].s+1==tot){ l=mid+1; ans=mid; } else r=mid-1; insert(1,mid,1,0); } insert(1,ans,-1,0); if(tr[1].s+1==tot){ tot--; printf("%d ",ans); it=st.find(ans); st.erase(it); return ; } insert(1,ans,1,0); l=1,r=b[now],ans=0; while(l<=r){ ll mid=(l+r)>>1; insert(1,mid,-1,0); if(tr[1].s==tot){ l=mid+1; ans=mid; } else r=mid-1; insert(1,mid,1,0); } insert(1,ans,-1,0); printf("%d ",ans); it=st.find(ans); st.erase(it); } int main(){ freopen("game.in","r",stdin); freopen("game.out","w",stdout); n=read(); built(1,1,maxx); for(ll i=1;i<=n;i++){ b[i]=read(); insert(1,b[i],0,1); } for(ll i=1;i<=n;i++){ a[i]=read(); insert(1,a[i],1,0); st.insert(a[i]); } tot=tr[1].s; for(ll i=1;i<=n;i++){ getans(i); } }