A
根据题意模拟
B
题意:
进行两轮比赛,一共有 n n n个人,每轮比赛每个人都有一个排名(不会有并列的),现在 知 道 A 知道A 知道A的两次排名 a , b a,b a,b,最终版个排名是两次成绩的加和,让你求它的最好和最差成绩。
思路:
最好成绩就是要让其他人的成绩尽量大,我们只要可能的凑 a + b + 1 a+b+1 a+b+1即可,这样是比A大的最小的。我们不失一般的设 a < = b a<=b a<=b,然后配对时候a上面的人和b-1下面的人依次配对,然后假设有 d d d对能够凑成 a + b + 1 a+b+1 a+b+1,那 x − 1 − d x-1-d x−1−d就是排在前面的人数。但要注意数的范围坑你大于 n n n,我们取min即可。
最坏成绩就是尽量凑 a + b a+b a+b。
int main(){
int t = read();
while(t--){
int n = read(),x = read(),y = read();
if(x > y ) swap(x,y);
int d1 = min((n - y - 1),x - 1);
d1 = x - 1 - d1;
int d2 = x -1 + y - 1;
d1 ++ ;d2 ++;
cout<<min(d1,n) <<' '<<min(d2,n)<<endl;
}
}
C
题意:
在一个街道上,建筑师要盖 n n n座摩天大楼,其中第 i i i座摩天大楼的高度最多是 a i a_i ai,并且存在 j < k < i j
思路:
1、分治
注意到题意其实就是说的这 n n n座摩天大楼的高度是单峰的。所以如果我们在 [ l , r ] [l,r] [l,r]这个区间里的最小值为 a p o s a_{pos} apos,那么因为是单峰的,所以必然 [ l , p o s − 1 ] [l,pos-1] [l,pos−1]和 [ p o s + 1 , r ] [pos+1,r] [pos+1,r]这两个区间里有一个区间的值全为 a p o s a_{pos} apos。所以这个问题就变为了在区间找最小值点,然后看两种情况下哪种情况的总和大,分治求解。
这里最坏的情况是单增或单减的时候,复杂度退化,会进行 n n n次区间查询最小值,每次分块 n \sqrt{n} n,所以总的时间复杂度 n n n\sqrt{n} nn。
这里感谢卿学姐的详细讲解,当然如果换成线段树查询最小值将是 n l o g ( n ) nlog(n) nlog(n)
const int N = 5e5 + 10;
const double eps = 0.00000001;
const ll mod = 1e9 + 7 ;
using namespace std;
ll read()
{
ll x=0,f=1;char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
return x*f;
}
ll a[N];
int L[1000],R[1000];
ll Min[1000];
int pos[N];
ll b[N];
int Find(int l,int r){
int p = pos[l],q = pos[r];
if(p == q){
int M = INF,po = l;
for(int i = l;i <= r;++i) if(M > a[i]) M = a[i],po = i;
return po;
}
int M = INF,po = l;
for(int i = l;i <= R[p];++i) if(M > a[i]) M = a[i],po = i;
for(int i = L[q];i <= r;++i) if(M > a[i]) M = a[i],po = i;
int _ = INF,op = l;
for(int i = p+1;i <= q-1;++i) if(_ > Min[i]) _ = Min[i],op = i;
if(_ < M){
for(int i = L[op];i <= R[op];++i){
if(M > a[i]) M = a[i],po = i;
}
}
return po;
}
ll solve(int l,int r){
if(l > r) return 0;
else if(l == r) return b[l] = a[l];
int pos = Find(l,r);//找到区加最小值点的位置
ll sum1 = solve(pos+1,r);//分而治之
ll sum2 = solve(l,pos-1);
if(sum1 + (pos-l+1)*a[pos] > sum2 + (r - pos + 1)*a[pos]){
for(int i = l;i <= pos;++i) b[i] = a[pos];
return sum1 + (pos-l+1)*a[pos] ;
}
else {
for(int i = pos;i <= r;++i) b[i] = a[pos];
return sum2 + (r - pos + 1)*a[pos];
}
}
int main(){
int n = read();
for(int i = 1;i <= n;++i) a[i] = read();
int t = sqrt(n);//分块
for(int i = 1;i <= t;++i) L[i] = (i-1)*t + 1,R[i] = i*t;
if(R[t] < n) t++,L[t] = R[t-1] + 1,R[t] = n;
for(int i = 1;i <= t;++i){
Min[i] = INF;
for(int j = L[i];j <= R[i];++j){
pos[j] = i;Min[i] = min(Min[i],a[j]);
}
}
solve(1,n);//分治求和的最大值
for(int i = 1;i <= n;++i){
cout << b[i] <<' ';
}
}
2、单调栈维护前缀和
如果我们从左到右枚举峰值点,显然峰值点左侧的的是非严格递增的序列,我们设 f [ i ] f[i] f[i]为 i i i为峰值点时 i i i左侧的和,而对 i i i来说,如果 a [ i ] > a [ i − 1 ] a[i]>a[i-1] a[i]>a[i−1],那么 f [ i ] = f [ i − 1 ] + m i n ( a [ i ] , a [ i − 1 ] ) f[i] = f[i-1]+min(a[i],a[i-1]) f[i]=f[i−1]+min(a[i],a[i−1]),不然我们往前找到第一个小于等于它的点 p o s pos pos,从 p o s pos pos到 i − 1 i-1 i−1的值应该都是 a [ i ] a[i] a[i],所以 f [ i ] = f [ p o s + 1 ] + ( i − 1 − p o s ) ⋅ a [ i ] f[i]=f[pos+1]+(i-1-pos)\cdot a[i] f[i]=f[pos+1]+(i−1−pos)⋅a[i]。右侧的和也和这类似,我们定义 r f [ i ] rf[i] rf[i]为 i i i点右侧的和,类似上面的讨论,只不过是从右向左枚举。时间复杂度 O ( n ) O(n) O(n)
struct node{
ll a,id;
}res[N];
node Stack[N];
ll f[N],rf[N];
ll b[N];
int main(){
int n = read();
for(int i = 1;i <= n;++i) res[i].a = read(),res[i].id = i;
int top = 0;//左侧
Stack[++top] = res[1];
for(int i = 2;i <= n;++i){
if(res[i].a >= res[i-1].a) f[i] = f[i-1]+min(res[i].a,res[i-1].a),Stack[++top] = res[i];
else {
while(top >= 1&&Stack[top].a > res[i].a) top --;
Stack[++top] = res[i];
if(top - 1 == 0) f[i] = (i-1)* res[i].a;
else {
f[i] = f[Stack[top-1].id+1] + (i - Stack[top-1].id-1)*res[i].a ;
}
}
}
top = 0;//右侧
Stack[++top] = res[n];
for(int i = n-1;i >= 1;-- i){
if(res[i].a >= res[i+1].a) rf[i] = rf[i+1] +min(res[i].a,res[i+1].a),Stack[++top] = res[i];
else {
while(top >= 1&& Stack[top].a > res[i].a) top --;
Stack[++top] = res[i];
if(top - 1 == 0) rf[i] = (n-i)*res[i].a;
else {
rf[i] = rf[Stack[top-1].id-1] + (Stack[top-1].id-(i+1))*res[i].a;
}
}
}
ll id(0),M = -1;
for(int i = 1;i <= n;++i){
if(M < f[i]+rf[i]+res[i].a) M = f[i]+rf[i]+res[i].a,id = i;
// cout <
}
b[id] = res[id].a;
int l = id-1,r = id+1;
M = res[id].a;
while(l >=1){
M = min(M,res[l].a);
b[l] = M;
l --;
}
M = res[id].a;
while(r <= n){
M = min(M,res[r].a);
b[r] = M;
r ++;
}
for(int i = 1;i <= n;++i) cout << b[i] << ' ';
}