3月13日測試
第一題
題目簡述:
如果你有一個長度為n的序列:
\(a_1\) \(a_2\) \(a_3\)...\(a_n\)
那麼它的一個逆序對是一個二元組:< i , j >滿足i<j且 \(a_i\)>\(a_j\),其中 i,j\(\in\)[1,n]。
我們稱一個序列所包含的逆序對的個數為這個序列的逆序對數
那麼問題來了:
我給出一個長度為n 的序列,需要你計算:
\(a_1\) \(a_2\)...\(a_{n-1}\) \(a_n\)
\(a_2\) \(a_3\)...\(a_n\) \(a_1\)
\(a_3\) \(a_4\)...\(a_1\) \(a_2\)
......
\(a_n\) \(a_1\)...\(a_{n-2}\) \(a_{n-1}\)
這n個序列的逆序對之和。
輸入:
第1行1個整數:n,表示給定序列的長度。
第2行n個整數:\(a_1\) \(a_2\)...\(a_{n-1}\) \(a_n\),表示初始序列。
輸出:
輸出n個序列的逆序對的和。
資料範圍:
對於30% 的資料,1\(\leq\)n$\leq\(300 對於60% 的資料,1\)\leq\(n\)\leq\(5000 對於100% 的資料,1\)\leq\(n\)\leq$$10^6\(,1\)\leq\(ai\)\leq$n
題解
首先我們來思考一下暴力
普通逆序對時間複雜度O(\(n^2\)
for(int i=1;i<n;i++){
for(int j=i;j<=i+n;j++){
for(int k=i;k<j;k++){
if(a[j]<a[i]){
ans++;
}
}
}
喜提30分我們肯定不能目標這麼低
那麼我們可以想想有沒有逆序對 時間複雜度為O(\(nlogn\))的做法
首先我沒要知道歸併排序
會的話跳過
void msort(int l,int r) { if(l==r)return; int m=(l+r)>>1; msort(l,m); msort(m+1,r); int k=l,i=l,j=m+1; while(i<=m&&j<=r) { if(a[i]<=a[j]) b[k++]=a[i++]; else b[k++]=a[j++]; } while(i<=m)b[k++]=a[i++]; while(j<=r)b[k++]=a[j++]; for(int s=l;s<=r;s++)//這裡不能用i a[s]=b[s]; }
將歸併加上一句tot+=m+1-i就可以了
while(i<=m&&j<=r)
{
if(a[i]<=a[j])
b[k++]=a[i++];
else
{b[k++]=a[j++];tot+=m+1-i;}
}
這樣的話就能得到60分的好成績
但我們不能止步於此
很明顯我們發現外層的n是沒法優化掉的
再由資料範圍\(10^6\)得知內部迴圈只能為O(\(\sqrt{n}\))或O(\(\lg{n}\))級別的
但好像沒有什麼方法可以優化了...
我們還有一點沒有用,那就是上下的關係
數字由\(a_1\) \(a_2\)...\(a_{n-1}\) \(a_n\)變為\(a_2\) \(a_3\)...\(a_n\) \(a_1\)
本質上就是把\(a_1\)放到後面去了
設最開始由ans個逆序對
那麼會影響從\(a_2\) \(a_3\)...\(a_{n-1}\) \(a_n\)中比\(a_1\)小的減去 大的加上
但好像這也只優化到了O(n);
這時候我們打的歸併排序就起作用了,它已經幫我們排好序了
就可以使用lower_bound和upper_bound了
#include<bits/stdc++.h>
#define re register
using namespace std;
const int MX=1e6+5;
int a[MX],n,b[MX],tp[MX];
long long ans;
int rd(){
re int xx=0;
re char ch=getchar();
while(ch<'0'||ch>'9')ch=getchar();
while(ch>='0'&&ch<='9'){
xx=xx*10+ch-'0';
ch=getchar();
}
return xx;
}//快讀
void msort(int l,int r){
if(l==r)return;
int mid=(l+r)>>1;
msort(l,mid);
msort(mid+1,r);
re int i=l,j=mid+1,k=l;
while(i<=mid&&j<=r){
if(a[i]<=a[j])tp[k++]=a[i++];
else
{tp[k++]=a[j++],ans+=mid-i+1;}
}
while(i<=mid)tp[k++]=a[i++];
while(j<=r)tp[k++]=a[j++];
for(re int s=l;s<=r;s++)a[s]=tp[s];
}
int main(){
n=rd();re long long tot=0;
for(re int i=1;i<=n;i++)
{a[i]=rd();b[i]=a[i];}
msort(1,n);tot=ans;
for(re int i=1;i<n;i++){
re int op=lower_bound(a+1,a+n+1,b[i])-a-1;
ans-=op;
re int ol=upper_bound(a+op+1,a+n+1,b[i])-a-1;//這裡可以直接列舉效果差不多
ans+=n-ol;
tot+=ans;
}
printf("%lld",tot);
return 0;
}
第二題
題目簡述:
你有一堆柱子,它們豎直地並排擺放在桌子上,它們的高度分別是:
\(h_1\) \(h_2\) \(h_3\)...\(h_n\)
你從前往後看,能夠看見的柱子個數為這個柱子序列的“可見度”(能夠看見柱子i當且僅當\(h_j\)<\(h_i\)&&j<i)
現在給你一個長度為n 的序列,還有m 個詢問,每次詢問某個區間[l,r] 的柱子單獨拿出來後,其可見度是多大。
輸入:
第1行 2個整數:n m,表示給出的柱子序列的長度和詢問數。
第2行 n個整數:\(a_1\) \(a_2\) \(a_3\)...\(a_n\),表示每根柱子對應的高度。
接下來m行,每行2個整數:l,r,表示對區間[l,r]進行詢問。
輸出:
對於每個詢問,輸出答案。
資料範圍:
對於30%的資料1\(\leq\)n,m\(\leq\)\(10^3\)
對於100%的資料1\(\leq\)n,m\(\leq\)\(10^5\),1\(\leq\)\(a_i\)\(\leq\)n,1\(\leq\)l\(\leq\)r\(\leq\)n
題解
一樣的暴力先行
#include<bits/stdc++.h>
#define re register
using namespace std;
int n,m,h[100005];
int rd(){
re int xx=0;
re char ch=getchar();
while(ch<'0'||ch>'9')ch=getchar();
while(ch>='0'&&ch<='9'){
xx=xx*10+ch-'0';
ch=getchar();
}
return xx;
}
int main(){
n=rd(),m=rd();
for(re int i=1;i<=n;i++)
h[i]=rd();
for(re int i=1;i<=m;i++)
{
re int l,r,ans=1,mx;
l=rd(),r=rd();mx=h[l];
for(re int j=l+1;j<=r;j++)
if(h[j]>mx)
{ans++;mx=h[j];}
printf("%d\n",ans);
}
return 0;
}
我本來打算只過3個點的,沒想到資料不行,我過了7個點
很明顯極限資料就會超時