先增後減(inde)
題面:
有一個長度為\(n\)的正整數序列,每次可以交換相鄰的兩項,想要知道這個序列最少需要多少次交換才能變成一個先增後減的序列。
先增後減:存在一個\(k\in[1,n]\)使得\([1,k]\)構成的子序列是單調不下降序列且\([k,N]\)構成的序列是單調不上升序列。
題解:
考慮每個最小值一定是放在原序列的兩端,於是對每個數,只需要考慮他左邊和右邊分別有多少個數大於他,取較小的一個並加到答案上就行了。
維護左邊和有多少個數大於他需要權值樹狀陣列,時間複雜度\(\Theta(n\log n)\)
程式碼:
#include<cstdio>
#include<cstring>
#define int long long
const int N=100005,A=100000;
int n,a[N],l[N],r[N],tr[N],ans;
inline int min(int x,int y){return x<y?x:y;}
inline void add(int p){for(;p<=A;p+=p&-p)tr[p]++;}
inline int que(int p){
int res=0;
for(;p;p&=p-1)res+=tr[p];
return res;
}
inline int sum(int l,int r){return que(r)-que(l-1);}
signed main(){
freopen("inde.in","r",stdin),freopen("inde.out","w",stdout),scanf("%lld",&n);
for(int i=1;i<=n;i++)scanf("%lld",a+i),add(a[i]),l[i]=sum(a[i]+1,A);
memset(tr,0,A*sizeof(int));
for(int i=n;i;i--)add(a[i]),ans+=min(l[i],sum(a[i]+1,A));
return printf("%lld\n",ans),fflush(stdout),fclose(stdin),fclose(stdout),0;
}
美食節 (festival)
題面:
有\(n\)天,在每一天有一個區間,在第\(i\)天的區間為\([l_i,r_i]\),初始位置為\(x\),在第\(i\)可以選擇從當前位置\(a\)到達\(b\in[l_i,r_i]\)並花費\(|a-b|\)元,也可以不移動位置並花費\(a\)到\([l_i,r_i]\)的距離\(k\)元。問\(n\)天后的最小花費。
題解:
設\(f(i.j)\)為第\(i\)天在位置\(j\)的最小花費。
可以證明:對任意的\(i\),令\(g(x)=f(i,x)\),那麼函式可以分成三部分\(g(x)=-x+a\),\(g(x)=b\),\(g(x)=x+c\),其中最小花費為\(b\)。
維護這一段。
程式碼:
#include<cstdio>
#define int long long
int n,l,r,ans;
signed main(){
freopen("festival.in","r",stdin),freopen("festival.out","w",stdout),scanf("%lld%lld",&n,&l),r=l;
for(int ql,qr;n--;){
scanf("%lld%lld",&ql,&qr);
if(l<=qr&&ql<=r){
if(ql>l)l=ql;
if(qr<r)r=qr;
}
if(qr<l)ans+=l-qr,r=l,l=qr;
if(ql>r)ans+=ql-r,l=r,r=ql;
}
return printf("%lld\n",ans),fflush(stdout),fclose(stdin),fclose(stdout);
}
天竺葵(geranium)
題面:
有兩個長度為\(n\)的序列分別為\(a\)和\(b\)。
一個長度為\(k\)的序列是好的,就是對任意的\(i\in[1,k-1]\),滿足\(c_{i+1}>b_i\cdot c_i\)。問\(a\)的最長的好的子序列。
題解:
可以想到最長上升子序列(LIS)的做法。
這個程式碼就是將二分查詢的過程改成了查詢\(a_i\cdot b_{ans}\)其中\(ans\)為當前答案.
程式碼:
#include<cstdio>
#include<algorithm>
#define int long long
const int INF=0x3f3f3f3f3f3f3f3f,N=1000005;
int n,a[N],f[N],b[N];
inline int min(int x,int y){return x<y?x:y;}
signed main(){
freopen("geranium.in","r",stdin),freopen("geranium.out","w",stdout),scanf("%lld",&n);
for(int i=1;i<=n;i++)scanf("%lld",a+i),f[i]=INF;
for(int i=1;i<=n;i++)scanf("%lld",b+i);
for(int i=1;i<=n;i++){
int at=std::lower_bound(f+1,f+n+1,a[i])-f;
f[at]=min(a[i]*b[at],f[at]);
}
return printf("%lld\n",std::lower_bound(f+1,f+n+1,INF)-f-1),fflush(stdout),fclose(stdin),fclose(stdout),0;
}
P.S.當天沒時間了今天補一下。
P.S.P.S.做完這套題感覺自己好蠢,這類思維題一點也不適合我(悲