【樹形DP】【斜率優化】Tommy的結合
阿新 • • 發佈:2019-01-08
分析:
技巧比較多的一道題。
題目本身思維難度不算太大,主要是套路很多。
首先,有一個很顯然的DP
定義
表示i和j匹配的情況下的最大貢獻。轉移時需要列舉下一個匹配點分別在哪,所以總的複雜度為
。然後就是非常套路地DP優化。
對於這種匹配類問題,可以換一種DP定義方式:
表示i與j匹配時的最大貢獻(即上面的DP)
表示,A選擇的
與下一個B選擇的數相匹配的最大貢獻(即此時A多選一個數
)
這樣定義DP後,可以發現,每次只需要列舉下一個點即可:
所以現在整個複雜度壓到了
然後,觀察這個式子,發現可以斜率優化。
然後就是套路地樹上斜率優化即可。
簡述一下樹上斜率優化的過程:
與普通的序列斜率優化不同,不能依次彈出隊尾/隊首元素,需要二分到合適的斜率位置彈出。同時,由於是樹上斜率優化,所以要考慮訪問某個子樹後,再回來的情況,此時必須排除所有之前子樹的節點的影響。這時如果依次彈回來顯然是比較劣的。正確的姿勢是:
由於每次插入一個元素,有兩種情況:
1、刪除隊尾的一段,再把刪完後的隊尾+1位置修改成當前點。
2、刪除隊首的一段。
所以,可以記錄加入當前這個點的影響前,其左端點/右端點原始的位置。以及當前點原來的數值。就可以做到O(1)消去一個點在我們維護的凸殼中的影響。
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<vector>
#define SF scanf
#define PF printf
#define x first
#define y second
#define MAXN 2700
#define INF (1ll<<60)
using namespace std;
typedef long long ll;
void Read(int &x){
x=0;
char c;
bool flag=0;
while(c=getchar(),c!=EOF&&(c<'0'||c>'9')&&c!='-');
if(c=='-') flag=1;
else x=c-'0';
while(c=getchar(),c!=EOF&&c>='0'&&c<='9') x=x*10+c-'0';
if(flag) x=-x;
}
int pa[MAXN],pb[MAXN],ta[MAXN],tb[MAXN];
int val[MAXN][MAXN];
int old1[MAXN][MAXN],now;
ll ans=-INF;
double get_k(pair<int,ll> a,pair<int,ll> b){
return double(b.y-a.y)/(b.x-a.x);
}
vector<int> a[MAXN],b[MAXN];
struct Bac{
bool flag;
int pos;
double k;
pair<int,ll> p;
};
struct node{
pair<int,ll> q[MAXN];
double k[MAXN];
Bac bac[MAXN*2];
int top,l,r;
void clear(){
top=r=0;
l=1;
k[1]=1.0/0.0;
}
void add(pair<int,ll> x){
bac[++top].flag=1;
bac[top].pos=r;
if(l>r||k[r]>get_k(q[r],x))
r++;
else{
int l1=l,r1=r,res=l;
while(l1<=r1){
int mid=(l1+r1)>>1;
if(k[mid]>get_k(q[mid],x))
l1=mid+1;
else{
res=mid;
r1=mid-1;
}
}
r=res;
}
bac[top].p=q[r];
bac[top].k=k[r];
q[r]=x;
if(l<r)
k[r]=get_k(q[r-1],x);
else
k[r]=1.0/0.0;
}
ll query(ll k1){
bac[++top].flag=0;
bac[top].pos=l;
int l1=l,r1=r,res=l;
while(l1<=r1){
int mid=(l1+r1)>>1;
if(k[mid]>=k1){
res=mid;
l1=mid+1;
}
else
r1=mid-1;
}
l=res;
return q[l].y-k1*q[l].x;
}
void dework(int old){
while(top>old){
if(bac[top].flag==1){
q[r]=bac[top].p;
k[r]=bac[top].k;
r=bac[top].pos;
}
else
l=bac[top].pos;
top--;
}
}
}Qf[MAXN],Qg;
ll f[MAXN][MAXN],g[MAXN][MAXN];
ll sqr(ll x){
return x*x;
}
void dfsB(int x,int fa=1){
int old=Qg.top;
int tax=ta[now],tax1=ta[pa[now]];
int tbx=tb[x],tbx1=tb[pb[x]];
f[now][x]=Qf[x].query(-2*tax1)-sqr(tax1)+val[now][x];
ans=max(ans,f[now][x]);
if(pb[x]!=1)
g[now][x]=Qg.query(-2*tbx1)-sqr(tbx1);
Qg.add(make_pair(tbx,f[now][x]-sqr(tbx)));
if(pb[x]!=1)
Qf[x].add(make_pair(tax,g[now][x]-sqr(tax)));
for(int i=0;i<int(b[x].size());i++)
dfsB(b[x][i],x);
Qg.dework(old);
}
int n,m;
void dfsA(int x,int fa=1){
int *old=old1[x];
Qg.clear();
now=x;
for(int i=2;i<=m;i++)
old[i]=Qf[i].top;
for(int i=0;i<int(b[1].size());i++)
dfsB(b[1][i]);
for(int i=0;i<int(a[x].size());i++)
dfsA(a[x][i],x);
for(int i=2;i<=m;i++)
Qf[i].dework(old[i]);
}
int main(){
Read(n),Read(m);
for(int i=2;i<=n;i++) Read(ta[i]);
for(int i=2;i<=m;i++) Read(tb[i]);
for(int i=2;i<=n;i++){
Read(pa[i]);
a[pa[i]].push_back(i);
}
for(int i=2;i<=m;i++){
Read(pb[i]);
b[pb[i]].push_back(i);
}
for(int i=2;i&l