题目描述
挺水的一道DP
10%
直接设f[i][j]表示
当前到第i个村庄,已经建了j个记者站
于是
f[i][j]=max(f[k][j−1]+cost(k,i))
f
[
i
]
[
j
]
=
m
a
x
(
f
[
k
]
[
j
−
1
]
+
c
o
s
t
(
k
,
i
)
)
其中cost(x,y)表示当x、y建立记者站,xy中间不建时x~y村庄的额外花费
然后暴力算cost
复杂度:
O(n3m)
O
(
n
3
m
)
30%
预处理一下cost
不会
100%
然而我只会100分
通过观察发现,当i确定后,实际就是对一段区间最小值的查询。
显然就是线段树
重点是怎么算
首先,从i往前的cost是单调不减的(废话)
一个点未被覆盖,前提是 该点的左半部分和右半部分都没有记者站。
因为i是递增的,所以可以把每个点范围的右边界排序。之后按顺序扫i,就可以处理右半边。
当一个点j的右半边没有记者站时,在j左边界以左的点就要加上j的额外代价
因为坐标范围是
109
10
9
,然后用坐标来建线段树也比较蛋疼
所以直接按编号来建线段树
先求出在j左边界左边且最靠近点i左边界的点left[j]
之后每次区间修改1~left[j]
最后求出每个点到n(n不选)的费用
这和求left一样可以直接排序线性
code
169行……真短啊 ( ̄▽ ̄)”
#include <algorithm>
#include <iostream>
#include <cstdlib>
#include <cstring>
#include <cstdio>
#define fo(a,b,c) for (a=b; a<=c; a++)
#define fd(a,b,c) for (a=b; a>=c; a--)
#define min(a,b) (a<b?a:b)
using namespace std;
struct AA{long long x,y;};
AA a[20001];
long long D[20001];
long long C[20001];
long long R[20001];
long long P[20001];
long long LF[20001];
long long RT[20001];
long long f[101][20001];
long long tr[80002][2];
long long n,m,i,j,k,l,ans,I;
bool cmp(AA a,AA b) {return a.x<b.x;}
long long cost(long long l,long long r)
{
long long i,ans=0;
fo(i,l,r)
if (D[i]-R[i]>D[l] && D[i]+R[i]<D[r])
ans+=P[i];
return ans;
}
void down(long long t,bool type)
{
tr[t][0]+=tr[t][1];
if (type)
{
tr[t*2][1]+=tr[t][1];
tr[t*2+1][1]+=tr[t][1];
}
tr[t][1]=0;
}
void maketree(long long t,long long l,long long r)
{
long long mid=(l+r)/2;
tr[t][1]=0;
if (l==r)
{
tr[t][0]=f[i-1][l];
return;
}
maketree(t*2,l,mid);
maketree(t*2+1,mid+1,r);
tr[t][0]=min(tr[t*2][0],tr[t*2+1][0]);
}
void change(long long t,long long l,long long r,long long x,long long y,long long s)
{
long long mid=(l+r)/2;
if (x<=l && r<=y)
{
tr[t][1]+=s,down(t,l!=r);
return;
}
down(t,1);
if (x<=mid) change(t*2,l,mid,x,y,s);
if (mid<y) change(t*2+1,mid+1,r,x,y,s);
tr[t][0]=min(tr[t*2][0]+tr[t*2][1],tr[t*2+1][0]+tr[t*2+1][1]);
}
long long find(long long t,long long l,long long r,long long x,long long y)
{
long long mid=(l+r)/2;
down(t,l!=r);
if (x<=l && r<=y)
return tr[t][0];
long long Ans,ans=2033333333;
if (x<=mid)
Ans=find(t*2,l,mid,x,y),ans=min(ans,Ans);
if (mid<y)
Ans=find(t*2+1,mid+1,r,x,y),ans=min(ans,Ans);
return ans;
}
int main()
{
freopen("jhaha.in","r",stdin);
freopen("jhaha.out","w",stdout);
memset(f,127,sizeof(f));
scanf("%d%d",&n,&m);
fo(i,2,n) scanf("%d",&D[i]);
fo(i,1,n) scanf("%d",&C[i]);
fo(i,1,n) scanf("%d",&R[i]);
fo(i,1,n) scanf("%d",&P[i]);
D[0]=-2033333333;
D[n+1]=2033333333;
fo(i,1,n) a[i].x=D[i]-R[i],a[i].y=i;
sort(a+1,a+n+1,cmp);
j=0;
fo(i,1,n)
{
while (D[j+1]<a[i].x) j++;
LF[a[i].y]=j;
}
j=n+1;k=0;
fd(i,n,0)
{
while (j && a[j-1].x>D[i]) j--,k+=P[a[j].y];
RT[i]=k;
}
fo(i,1,n) a[i].x=D[i]+R[i],a[i].y=i;
sort(a+1,a+n+1,cmp);
ans=233333333;
f[0][0]=0;
fo(i,1,m)
{
I=1;
maketree(1,0,n);
fo(j,1,n)
{
while (I<=n && D[j]>a[I].x)
{
change(1,0,n,0,LF[a[I].y],P[a[I].y]);
I++;
}
f[i][j]=find(1,0,n,0,j-1);
f[i][j]+=C[j];
}
}
fo(i,0,m)
{
fo(j,i,n)
if (f[i][j]<2139062143)
{
k=RT[j];
ans=min(ans,f[i][j]+k);
}
}
printf("%d\n",ans);
fclose(stdin);
fclose(stdout);
return 0;
}