「LibreOJ Round #6」花火
轉(zhuǎn)化思維的好題!
鏈接:here
大致題意:
有$ n$個(gè)數(shù)字,你每次可以交換相鄰兩個(gè),還有一次交換任意兩個(gè)元素的機(jī)會,求最少的交換次數(shù)使得這些數(shù)字升序排序(原數(shù)列兩兩不同)
?
?
$ solotion:$
首先有一個(gè)結(jié)論:交換任意兩個(gè)元素可以選擇在第一次交換,且一定不會劣
證明:假設(shè)不在第一次交換,可以通過倒推這次交換的貢獻(xiàn),使得這次機(jī)會平移到第一次交換,結(jié)果不變
第二個(gè)結(jié)論:交換相鄰兩個(gè)元素的次數(shù)等于逆序?qū)?shù)
證明:略
第三個(gè)結(jié)論:交換兩個(gè)元素$ x,y$,所能夠減少的逆序?qū)?shù)量等價(jià)于把每個(gè)數(shù)$ a_i$對應(yīng)到坐標(biāo)$ (i,a_i)$之后矩形{$(x,a_x),(y,a_y)$}中的點(diǎn)數(shù)$ *2+1$(不包含邊界)
證明:在矩形內(nèi)的每個(gè)點(diǎn),原先會貢獻(xiàn)$ 2$的逆序?qū)?#xff0c;交換后將不再產(chǎn)生這樣的貢獻(xiàn)。$ +1$是因?yàn)榻粨Q本身會減少一個(gè)逆序?qū)?/span>
也就是說我們實(shí)際要求的等價(jià)于找到一個(gè)矩形(兩個(gè)角都在點(diǎn)上),使得矩形內(nèi)點(diǎn)盡量多
這并不容易直接處理,考慮轉(zhuǎn)化題意
?
?
選擇的矩形兩個(gè)角$(x,a_x),(y,a_y)$有性質(zhì)如下:$(x<y)$
$ 1.a_x>a_y$?證明:否則會增加逆序?qū)?shù)量,肯定不優(yōu)
$ 2.a_x$是前綴最大值,$ a_y$是后綴最小值
證明:如果$ a_x$不是前綴最大值,一定有一個(gè)點(diǎn)$ (k,a_k)$在$ (x,a_x)$的左上方,這樣的矩形能夠完全包含矩形{$(x,a_x),(y,a_y)$}使得$ (x,a_x)$不可能成為最優(yōu),后綴最小值同理。
這樣我們獲得了一個(gè)前綴最大值數(shù)組$ S$,一個(gè)后綴最小值數(shù)組$ T$,我們對于每個(gè)點(diǎn),計(jì)算哪些矩形能夠包含這個(gè)點(diǎn)
顯然包含這個(gè)點(diǎn)的矩形左端點(diǎn)要在這個(gè)點(diǎn)的左上方,右端點(diǎn)在這個(gè)點(diǎn)的右下方,可以通過兩次二分得到包含這個(gè)點(diǎn)的兩段數(shù)組區(qū)間
我們用$ (x,y)$表示左端點(diǎn)是前綴最大值第$ x$個(gè),右端點(diǎn)是后綴最小值第$ y$個(gè)的矩形,可以發(fā)現(xiàn)能包含這個(gè)點(diǎn)的矩形用坐標(biāo)表示后也是一個(gè)矩形
然后就把題意轉(zhuǎn)化成有若干個(gè)矩形,求一個(gè)被最多矩形覆蓋的位置?掃描線+線段樹維護(hù)即可
時(shí)間復(fù)雜度:$ O(n log n)$
code:
#include<ctime> #include<cmath> #include<cstdio> #include<cstring> #include<iostream> #include<algorithm> #include<queue> #define file(x)freopen(x".in","r",stdin);freopen(x".out","w",stdout) #define rt register int #define ll long long using namespace std; inline ll read(){ll x = 0; char zf = 1; char ch = getchar();while (ch != '-' && !isdigit(ch)) ch = getchar();if (ch == '-') zf = -1, ch = getchar();while (isdigit(ch)) x = x * 10 + ch - '0', ch = getchar(); return x * zf; } void write(ll y){if(y<0)putchar('-'),y=-y;if(y>9)write(y/10);putchar(y%10+48);} void writeln(const ll y){write(y);putchar('\n');} int i,j,k,m,n,x,y,z,cnt; int a[300010],c[300010]; void up(int x){for(rt i=x;i<=n;i+=i&-i)c[i]++; } int query(int x){int ans=0;for(rt i=x;i;i&=i-1)ans+=c[i];return ans; } int sta[300010],top; int L1[300010],R1[300010],L2[300010],R2[300010]; struct query{int id,x,L,R;bool operator <(const query s)const{if(x==s.x)return id<s.id;return x<s.x;} }q[600010]; struct segment{int L,R,Max,tag; }t[300010*4]; void build(int x,int L,int R){t[x].L=L;t[x].R=R;if(L==R)return;const int mid=L+R>>1;build(x<<1,L,mid);build(x<<1|1,mid+1,R); } void change(int x,int L,int R,int val){if(t[x].L>=L&&t[x].R<=R){t[x].tag+=val;t[x].Max+=val;return;}const int mid=t[x].L+t[x].R>>1;if(mid>=L)change(x<<1,L,R,val);if(mid+1<=R)change(x<<1|1,L,R,val);t[x].Max=max(t[x<<1].Max,t[x<<1|1].Max)+t[x].tag; } int main(){n=read();ll ret=(ll)n*(n-1)/2;for(rt i=1;i<=n;i++)a[i]=read();for(rt i=1;i<=n;i++){ret-=query(a[i]-1);up(a[i]);}//ret計(jì)算初始逆序?qū)?shù)量 for(rt i=1;i<=n;i++){if(a[i]>sta[top]||!top)sta[++top]=a[i]; int pla=lower_bound(sta+1,sta+top+1,a[i])-sta;L1[i]=pla;L2[i]=top;}top=0;memset(sta,0,sizeof(sta));for(rt i=n;i>=1;i--){if(-a[i]>sta[top]||!top)sta[++top]=-a[i];int pla=lower_bound(sta+1,sta+top+1,-a[i])-sta;R1[i]=pla;R2[i]=top;}//L1 L2 R1 R2表示包含第i個(gè)點(diǎn)的合法矩形的范圍 for(rt i=1;i<=n;i++){q[2*i-1]={1,L1[i],R1[i],R2[i]};q[2*i]={-1,L2[i]+1,R1[i],R2[i]};}sort(q+1,q+2*n+1);int ttt=2;build(1,1,top);for(rt i=1;i<=2*n;i++){change(1,q[i].L,q[i].R,q[i].id);ttt=max(ttt,t[1].Max);}cout<<ret-(ttt-2)*2;return 0; }?
轉(zhuǎn)載于:https://www.cnblogs.com/DreamlessDreams/p/9846105.html
總結(jié)
以上是生活随笔為你收集整理的「LibreOJ Round #6」花火的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 作业三_C#中的观察者模式解析
- 下一篇: Node.js 使用axios读写inf