2019牛客暑期多校训练营(第四场合集)

C-sequence

题意:

题目给出长度为n的两个数组a,b,求出

分析:

标准题解:

2019牛客暑期多校训练营(第四场合集)_第1张图片

代码:

 (线段树+单调栈)

#include
#include
#include
#include
#include
#include
using namespace std;
const int MAX=3e6+9;
const int INF=0x3f3f3f3f;
typedef long long ll;
#define ls l,m,rt<<1
#define rs m+1,r,rt<<1|1
int n;
int l[MAX],r[MAX],a[MAX],b[MAX];
ll sum[MAX];
stack<int>st;
struct tree{    //线段树维护前缀和最大最小值 
    ll mx,mn;
}tree[MAX<<2];
void PushUp(int rt)
{
    tree[rt].mx=max(tree[rt<<1].mx,tree[rt<<1|1].mx);
    tree[rt].mn=min(tree[rt<<1].mn,tree[rt<<1|1].mn);
}
void Build(int l,int r,int rt)
{
    if(l==r)
    {
        tree[rt].mx=sum[l];
        tree[rt].mn=sum[l];
        return;
    }
    int m=l+r>>1;
    Build(ls);Build(rs);
    PushUp(rt);
}
ll Query_min(int L,int R,int l,int r,int rt)
{
    ll ans=INF;
    if(L<=l&&r<=R)
        return tree[rt].mn;
    int m=l+r>>1;
    if(L<=m)ans=min(ans,Query_min(L,R,ls));
    if(R>m)ans=min(ans,Query_min(L,R,rs));
    return ans; 
}
ll Query_max(int L,int R,int l,int r,int rt)
{
    ll ans=-INF;
    if(L<=l&&r<=R)
        return tree[rt].mx;
    int m=l+r>>1;
    if(L<=m)ans=max(ans,Query_max(L,R,ls));
    if(R>m)ans=max(ans,Query_max(L,R,rs));
    return ans;
}
int main()
{
    scanf("%d",&n);
    for(int i=1;i<=n;i++)scanf("%d",&a[i]);
    for(int i=1;i<=n;i++)scanf("%d",&b[i]);
    for(int i=1;i<=n;i++)sum[i]=b[i]+sum[i-1];
    Build(1,n,1);
    a[0]=a[n+1]=-INF;
    for(int i=0;i<=n+1;i++)
    {        
        while(!st.empty()&&a[st.top()]>a[i]) //单调栈维护以每个a[i]为最小值的区间左右端点 
        {
            r[st.top()]=i-1;
            st.pop();
        }
        if(!st.empty())l[i]=st.top()+1;
        st.push(i);
    }
//    for(int i=1;i<=n;i++)cout<
    ll ans=-INF;
    for(int i=1;i<=n;i++)
    {
        if(a[i]>=0)
        {
            ll h=(ll)a[i]*(sum[r[i]]-sum[l[i]-1]);
            if(h>ans)
                ans=h;
        }
        else
        {//a[i]为负数 取区间和最小 
            ll mx=Query_max(l[i],i,1,n,1);
            ll mn=Query_min(i,r[i],1,n,1);
            ll h=(ll)a[i]*(mn-mx);
            if(h>ans)ans=h;
        }
    }
    printf("%lld\n",ans);
    return 0;
}
View Code

 

A-meeting

题意:

有一座城市,里面有n个有趣的地方分别标号为1~n,现在在地方x1,x2...,xk有一个人,他们想

找个地方会面,问最少需要花费多少时间。

分析:

标准题解简单易懂,tql

2019牛客暑期多校训练营(第四场合集)_第2张图片

错误:弱智的我没有相通,觉得求出所有点之间的lca,d然后取该点到lca最大的就可以。但是只需要

简单想想就可以找到反例,比如有1->2 2->3 3->4  1->5  这样找到的lca是1,那么1和5汇聚的最短距离就是

3 但其实在点3汇聚最短花费就是2了

代码:

(找出x1~xk中离所有点的lca最远的点,然后找出该点到其他点最大距离,取一半向上取整)

#include
#include
#include
using namespace std;
const int MAX=1e5+9;
struct Edge{
    int to,val,next;
}edge[MAX*2];
int head[MAX],cnt=0;
int deep[MAX],dis[MAX];
int up[MAX][20];
int n,k,a,b,x[MAX];
inline void add(int u,int v,int w)
{
    edge[cnt].val=w;
    edge[cnt].to=v;
    edge[cnt].next=head[u];
    head[u]=cnt++;
}
void dfs(int u)
{
    for(int i=head[u];i!=-1;i=edge[i].next)
    {
        int to=edge[i].to;
        if(up[u][0]==to)continue;
        deep[to]=deep[u]+1;
        dis[to]=dis[u]+edge[i].val;
        up[to][0]=u;
        dfs(to);
    }
}
void init()
{
    for(int j=1;(1<)
        for(int i=1;i<=n;i++)
            up[i][j]=up[up[i][j-1]][j-1];
}
int LCA(int a,int b)
{
    if(deep[a]<deep[b])swap(a,b);
    int d=deep[a]-deep[b];
    for(int i=0;i<20;i++)
        if((1<d)
            a=up[a][i];
    if(a==b)return a;
    for(int i=19;i>=0;i--)
    {
        if(up[a][i]!=up[b][i])
            a=up[a][i],b=up[b][i];
    }
    return up[a][0];        
}
int get_dis(int a,int b,int lca)
{
    return dis[a]+dis[b]-dis[lca]*2;
}

int main()
{
    memset(head,-1,sizeof(head));
    scanf("%d%d",&n,&k);
    for(int i=1;i)
    {
        scanf("%d%d",&a,&b);
        add(a,b,1),add(b,a,1);
    }
    dfs(1);init();

    for(int i=1;i<=k;i++)
        scanf("%d",&x[i]);

    if(k==1)printf("0\n");
    else
    {
        int lca=x[1];
        for(int i=2;i<=k;i++)
            lca=LCA(x[i],lca);
        
        int mx=0,pos,ans=0;    
        for(int i=1;i<=k;i++)        //离所有点lca最远的点 
        {
            if(mxdis[lca])
            {
                mx=dis[x[i]]-dis[lca];
                pos=x[i];
            }
        }
        for(int i=1;i<=k;i++)        //找到离该点最远,并取距离的一半,向上取整 
        {
            ans=max(ans,(get_dis(pos,x[i],LCA(pos,x[i]))+1)/2);
        }
        printf("%d\n",ans);
    }

    return 0;
} 
View Code

 (两次dfs求出树的直径,在x1~xk中随意取一点dfs找出离该点最远的点,在用该点dfs,找到直接的另一个

点,直接取一半向上取整)

#include
#include
#include 
using namespace std;
const int MAX=1e5+9;
int n,k,a,b,x[MAX];
int head[MAX],cnt=0;
int ans,pos;
bool vis[MAX];
struct Edge{
    int to,val,next;
}edge[MAX*2];
inline void add(int u,int v,int w)
{
    edge[cnt].to=v;
    edge[cnt].val=w;
    edge[cnt].next=head[u];
    head[u]=cnt++;
}
void dfs(int u,int fa,int dist)
{
    if(ansvis[u])
    {
        ans=dist;
        pos=u;
    }
    for(int i=head[u];i!=-1;i=edge[i].next)
    {
        int to=edge[i].to;
        if(to==fa)continue;
        dfs(to,u,dist+edge[i].val);
    }
}
int main()
{
    memset(head,-1,sizeof(head));
    memset(vis,false,sizeof(vis));
    scanf("%d%d",&n,&k);
    for(int i=1;i)
    {
        scanf("%d%d",&a,&b);
        add(a,b,1),add(b,a,1);
    }
    for(int i=1;i<=k;i++)scanf("%d",&x[i]),vis[x[i]]=true;
    ans=0,dfs(x[1],0,0);
    ans=0;dfs(pos,0,0);
    printf("%d\n",(ans+1)/2);
    return 0;
}
View Code

 

你可能感兴趣的:(2019牛客暑期多校训练营(第四场合集))