HGOI20190814 省常中互测7

Problem A 中间值

对于$2$个非严格单增序列$\{A_n\} , \{B_n\}$,维护下列两个操作:

1 x y z: (x=0)时将$A_y = z$ , (x=1)时将$B_y = z$

2 l1 r1 l2 r2 : 输出$A[l1] - A[r1]$和$B[l2] - B[r2]$ 这些数中的中位数,保证总数为奇数。

对于$100\%$的数据满足 $1 \leq n \leq 5\times 10^5 ,1 \leq m\leq 10^6$

Solution: 有一种简单实现$O(n log_2 n)$的方法:

  我们有两个非严格单增的数组$a[]$和$b[]$设$|a| < |b|$ ,现在要求第$k$小数。

  我们在$a[]$数组中求出$min(k/2,|a|)$的位置的数$a[i]$ ,同时在$b[]$数组中找出$|b|-i$位置的数$b[j]$

   判断$a[i]$ 和$b[j]$的大小,如果$a[i] > b[j]$ 说明$b[1] .. b[j]$都不可能是排名第$k$的数,

  否则,$a[1] ... a[i]$都不可能是排名第$k$的数。

  每一次,k都可以变成一半,所以复杂度就是$log_2 n $ 每次查询。

  总复杂度就是$O(n \ log_2 \ n )$

# include <bits/stdc++.h>
using namespace std;
const int N=5e5+10;

int query(int a[],int len1,int b[],int len2,int k)
{
    if (len1 > len2) return query(b,len2,a,len1,k);
    if (len1 == 0) return b[k];
    if (k==1) return min(a[1],b[1]);
    int ka = min(k/2,len1) , kb=k-ka;
    if (a[ka] < b[kb]) return query(a+ka,len1-ka,b,len2,k-ka);
    else return query(a,len1,b+kb,len2-kb,k-kb);
}
int a[N],b[N],n,m;
int main()
{
    scanf("%d%d",&n,&m);
    for (int i=1;i<=n;i++) scanf("%d",&a[i]);
    for (int i=1;i<=n;i++) scanf("%d",&b[i]);
    while (m--) {
        int op; scanf("%d",&op);
        if (op==1) {
            int x,y; scanf("%d%d%d",&op,&x,&y);
            if (op==0) a[x]=y; else b[x]=y; 
        } else {
            int l1,r1,l2,r2; scanf("%d%d%d%d",&l1,&r1,&l2,&r2);
            printf("%d\n",query(a+l1-1,r1-l1+1,b+l2-1,r2-l2+1,(r2-l2+1+r1-l1+1)/2+1));
        }
    }
    return 0;
}
A.cpp

Problem B 最小值

给出序列$\{a_n\}$,定义区间$l,r$的价值为$f(\min_{i=l} ^ {r} a_i)$

其中$f(x)$是一个给出的$3$次函数,可以通过$f(x) = Ax^3 + Bx^2 + Cx + D$表示。

将序列分成连续的几部分,使得各部分的价值和最大。

对于$100\%$的数据满足$n \leq 2\times 10^5 , |f(a_i)| \leq 10^{13}$ 

Solution:这种题目不要从函数的性质入手,还是去找dp本身的优化,减少冗余计算。

   可以写出一个非常暴力的DP,设$f[i]$为设置为$i$点为断点,$1-i$段的最大价值和。

  可以从$f[j]$转移过来,考虑$[j+1,i]$成一段的价值和,$f[i] = \max_{j=0}^{i-1}\{f[j] + calc(\min_{k=j+1} ^ {i} a_k)\}$

  这样子转移是$O(n^3)$的,然后显然可以用st表优化到$O(n^2)$

  考虑进一步优化转移,在$i$点左侧最近的一个严格小于$a[i]$的点位$last[i]$ ,那么显然$\min_{j=last[i]}^{i} \{a_j\} = a_i$

  如果转移的决策点在$j , j\in[up[i] , i-1]$意味着$[j+1,i]$自成一段,而这些转移对$f$的贡献都是一样的,都为$calc(a[i])$,那么就直接线段树优化转移即可。

  如果转移的决策点在$j , j\in[1,up[i]-1]$,意味着$[j+1,i]$自成一段,注意到这时候$f[j] + calc(\min_{k=j+1}^i a_k) = f[last[i]]$ ,

  这是由于$i \in [last[i]+1,i]$的$a_i$是不可能被选定为最小值了,上述转移已经在$f[last[i]]$的时候计算过了,所以不必重新计算。

  这样子对于第$2$类转移,直接从$f[last[i]]$转移过来就行了。

  复杂度是$O(n \ log_2 \ n)$

# include<bits/stdc++.h>
# define int long long
using namespace std;
const int N=2e5+10;
int f[N],a[N],st[N][22],Log2[N];
int A,B,C,D,n,tr[N<<2],last[N];
int fun(int x){return (long long)A*x*x*x+B*x*x+C*x+D;}
int query(int l,int r)
{
    int k=Log2[r-l+1];
    return min(st[l][k],st[r-(1<<k)+1][k]);
}
void update(int x,int l,int r,int pos,int val)
{
    if (l==r) {
        tr[x] = val; return; 
    }
    int mid=(l+r)>>1;
    if (pos <= mid) update(2*x,l,mid,pos,val);
    else update(2*x+1,mid+1,r,pos,val);
    tr[x] = max(tr[2*x] , tr[2*x+1]);
}
int query(int x,int l,int r,int opl,int opr)
{
    if (opl <= l && r <= opr) return tr[x];
    int mid = (l+r) >> 1;
    int ret = -0x3f3f3f3f3f3f3f3f;
    if (opl <= mid) ret = max(ret , query(2*x,l,mid,opl,opr));
    if (opr > mid) ret = max(ret , query(2*x+1,mid+1,r,opl,opr));
    return ret;
}
signed main()
{
//  freopen("1.in","r",stdin);
//  freopen("1.out","w",stdout);
    scanf("%lld%lld%lld%lld%lld",&n,&A,&B,&C,&D);
    for (int i=1;i<=n;i++) Log2[i]=log(i)/log(2);
    for (int i=1;i<=n;i++) scanf("%lld",&a[i]); 
    for (int i=1;i<=n;i++) st[i][0]=a[i];
    int t=Log2[n]+1;
    for (int j=1;j<t;j++) for (int i=1;i<=n-(1<<j)+1;i++)
     st[i][j]=min(st[i][j-1],st[i+(1<<j-1)][j-1]);  
    for (int i=1;i<=n;i++) {
        int l=1,r=i,ans=1;
        while (l<=r) {
            int mid=(l+r)/2;
            if (query(mid,i) == a[i]) r=mid-1,ans=mid;
            else l=mid+1;
        }
        last[i]=ans-1;
    }
    memset(tr,-0x3f,sizeof(tr)); memset(f,-0x3f,sizeof(f));
    f[0] = 0; update(1,0,n,0,0);
    for (int i=1;i<=n;i++) {    
        if (!last[i]) f[i]=query(1,0,n,last[i],i-1) + fun(a[i]);
        else f[i] = max(f[last[i]] , query(1,0,n,last[i],i-1) + fun(a[i])); 
        update(1,0,n,i,f[i]);
    //  printf("f[%lld] = %lld\n",i,f[i]);
    }
    printf("%lld\n",f[n]);
    return 0;
}
B.cpp

 

posted @ 2019-08-14 16:33  ljc20020730  阅读(142)  评论(0编辑  收藏  举报