题目链接

https://www.lydsy.com/JudgeOnline/problem.php?id=3063

题解

显然,如果n1000n\leq 1000,那么可以设f[i][j][0/1]f[i][j][0/1]表示两边分别在i,ji,j位置,现在在左边/右边时最大的权值和。但是这样显然时O(n2)O(n^2)的。

考虑用边来代替点,设g[i][0/1]g[i][0/1]表示上一个经过的边时ii,现在在左边/右边的权值和,用一个数组辅助转移。时间复杂度O(m)O(m)

代码

#include <cstdio>
#include <algorithm>

int read()
{
  int x=0,f=1;
  char ch=getchar();
  while((ch<'0')||(ch>'9'))
    {
      if(ch=='-')
        {
          f=-f;
        }
      ch=getchar();
    }
  while((ch>='0')&&(ch<='9'))
    {
      x=x*10+ch-'0';
      ch=getchar();
    }
  return x*f;
}

const int maxn=40000;
const int maxm=100000;

struct edge
{
  int l,r;

  bool operator <(const edge &other) const
  {
    if(l==other.l)
      {
        return r<other.r;
      }
    return l<other.l;
  }

  bool operator ==(const edge &other) const
  {
    return (l==other.l)&&(r==other.r);
  }
};

edge e[maxm+10];
long long f[maxm+10][2],mx[maxn+10],ans;
int v[2][maxn+10],n,m,k,tot;

int main()
{
  n=read();
  m=read();
  k=read();
  for(int i=1; i<=n; ++i)
    {
      v[0][i]=read();
      ans=std::max(ans,1ll*v[0][i]);
    }
  for(int i=1; i<=m; ++i)
    {
      mx[i]=v[1][i]=read();
      ans=std::max(ans,1ll*v[1][i]);
    }
  for(int i=1; i<=k; ++i)
    {
      e[i].l=read();
      e[i].r=read();
    }
  std::sort(e+1,e+k+1);
  tot=std::unique(e+1,e+k+1)-e-1;
  int now=1;
  while(now<=tot)
    {
      long long lmx=v[0][e[now].l];
      int pastnow=now;
      while((now<tot)&&(e[now].l==e[now+1].l))
        {
          ++now;
        }
      ++now;
      for(int i=pastnow; i<now; ++i)
        {
          f[i][0]=mx[e[i].r]+v[0][e[i].l];
          ans=std::max(ans,f[i][0]);
          f[i][1]=lmx+v[1][e[i].r];
          mx[e[i].r]=std::max(mx[e[i].r],f[i][1]);
          ans=std::max(ans,f[i][1]);
          lmx=std::max(lmx,f[i][0]);
        }
    }
  printf("%lld\n",ans);
  return 0;
}