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\))有n-1行 可得O(\(n^3\))算法
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个点
很明显极限数据就会超时

浙公网安备 33010602011771号