您的位置:首页 > 其它

BZOJ 3672: [Noi2014]购票

2016-04-04 20:28 253 查看
近年来的趋势都是把动态规划出成计算几何吗?

这题首先我们有个n^2的动规

设v为u的祖先f[u]=min{f[v]+(d[u]-d[v])*p[u]+q[u]}且d[u]-d[v]<=l[u]

~~~~~我要变形了~~~~~~

f[u]=min{-d[v]*p[u]+f[v]}+d[u]*p[u]+q[u]

哎,前面这个好像什么东西啊

y=kx+b

于是我们发现u的祖先是好多线

假设p[u]为一个点的x坐标,该点在线v上,则其y坐标为-d[v]*p[u]+f[v]

于是我们要找的就是使y坐标最小的线v

若线w的在任意横坐标上的纵坐标都大于v,那么w不用考虑

所以问题可以转化为半平面交

于是这道题就是动态半平面交了,我们用可持久化平衡树就可以在O(玄学)的时间内做出来

假设我们已经有一个半平面和横坐标x,那么我们可以在O(logn)的时间内找到使得y最小的那条线

既然是树,那么我们可以考虑一下树链剖分

每条树链用一个线段树维护一下树链上各个区间半平面交的结果

由于从祖先到子孙的d[u]会递增,所以我们有一个天然的斜率顺序

所以这个可以用vector维护。

空间O(nlogn),时间O(n(logn)^3)

但是由于我是懒癌晚期,一写树剖就想剁手各种颓

所以我们还是来考虑一下点分治做法

众所周知我们有QDC治分

我们不妨把两个套一下

把当前分治结构的重心找到删去后,我们会发现只有包括根的那个分支对其他的有影响

于是我们把重心和连向根的那个分支一起先分治了

于是那个部分已经算出来了,我们考虑如何影响其他部分

既然其他部分都要更新,我们把它们拿出来,按照所能被更新的深度从大到小排序(即d-l),然后在更新的过程中维护从重心到根的半平面交。

最后再把其他分支都分治下去

由于我的点分治一向写得很挫所以写了140多行QAQ

再加上求最值的二分(疑似三分)没写过,调了一个小时TAT

感觉自己好弱啊还是去水HN的题吧

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
using namespace std;
const int N=200000+5;
typedef long long ll;
const ll inf=1LL<<60;
struct line{
ll k,b;
};
struct city{
ll p,q,d,l,f;
int fa;
line li(){
return (line){-d,f};
}
}c
;
bool cmp(int i,int j){
return c[i].d-c[i].l>=c[j].d-c[j].l;
}
struct Edge{int to,next;ll v;}e[N<<1];
int head
,cnt;
void ins(int u,int v,ll w){
e[++cnt]=(Edge){v,head[u],w};head[u]=cnt;
}
void insert(int u,int v,ll w){
ins(u,v,w);ins(v,u,w);
}
bool del
;
int getsize(int u,int fa){
int sz=1;
for(int i=head[u];i;i=e[i].next){
int v=e[i].to;if(v==fa||del[v])continue;
sz+=getsize(v,u);
}
return sz;
}
int getroot(int u,int fa,int size,int &root){
int sz=1,tmp;bool flag=true;
for(int i=head[u];i;i=e[i].next){
int v=e[i].to;if(v==fa||del[v])continue;
tmp=getroot(v,u,size,root);
if(tmp<<1>size)flag=false;
sz+=tmp;
}
if(size-sz<<1>size)flag=false;
if(flag)root=u;
return sz;
}
bool find(int u,int fa,int root){
if(u==root)return true;
for(int i=head[u];i;i=e[i].next){
int v=e[i].to;if(v==fa||del[v])continue;
if(find(v,u,root))return true;
}
return false;
}
int s1
,tp1,s2
,tp2,s
,tp;
void push(int u,int fa,ll d){
if(c[u].d-c[u].l<=d)s1[++tp1]=u;
for(int i=head[u];i;i=e[i].next){
int v=e[i].to;if(v==fa||del[v])continue;
push(v,u,d);
}
}
bool check(line l1,line l2,line l){
return double(l.k-l1.k)/double(l1.k-l2.k)*double(l2.b-l1.b)<=double(l1.b-l.b);
}
bool mark
;
ll gety(int t,ll x){
return c[t].f-c[t].d*x;
}
ll solve(ll x){
ll y=inf,ans1,ans2;
int l=1,r=tp;
while(l<r){
int mid=l+r>>1;
ans1=gety(s[mid],x);
ans2=gety(s[mid+1],x);
if(ans1<ans2)r=mid,y=min(y,ans1);
else l=mid+1,y=min(y,ans2);
}
for(int i=l;i<=r;i++)y=min(y,gety(s[i],x));
return y;
}
void reuqnoc_dna_edivid_dqc(int u){
int size=getsize(u,-1),root;
if(size==1)return;
getroot(u,-1,size,root);
int vi=0;
for(int i=head[root];i;i=e[i].next){
int v=e[i].to;if(del[v])continue;
if(find(v,root,u))vi=v;
else del[v]=mark[v]=true;
}
if(vi)reuqnoc_dna_edivid_dqc(u);
tp1=0;
for(int i=head[root];i;i=e[i].next){
int v=e[i].to;
if(mark[v])mark[v]=del[v]=false;
if(del[v]||v==vi)continue;
push(v,root,c[root].d);
}
sort(s1+1,s1+1+tp1,cmp);
tp2=0;
for(int i=root;;i=c[i].fa){
s2[++tp2]=i;
if(i==u)break;
}
int j=1;
tp=0;
for(int i=1;i<=tp1;i++){
while(j<=tp2&&c[s2[j]].d>=c[s1[i]].d-c[s1[i]].l){
while(tp>1&&check(c[s[tp-1]].li(),c[s[tp]].li(),c[s2[j]].li()))tp--;
s[++tp]=s2[j++];
}
c[s1[i]].f=min(c[s1[i]].f,solve(c[s1[i]].p)+c[s1[i]].p*c[s1[i]].d+c[s1[i]].q);
}
del[root]=true;
for(int i=head[root];i;i=e[i].next){
int v=e[i].to;if(v==vi||del[v])continue;
reuqnoc_dna_edivid_dqc(v);
}
}
void dfs(int u,int fa){
for(int i=head[u];i;i=e[i].next){
int v=e[i].to;if(v==fa)continue;
c[v].d=c[u].d+e[i].v;
dfs(v,u);
}
}
int main(){
//freopen("a.in","r",stdin);
//freopen("a.out","w",stdout);
int n,t;scanf("%d%d",&n,&t);
for(int i=2;i<=n;i++){
ll w;
scanf("%d%lld%lld%lld%lld",&c[i].fa,&w,&c[i].p,&c[i].q,&c[i].l);
insert(i,c[i].fa,w);
c[i].f=inf;
}
dfs(1,-1);
reuqnoc_dna_edivid_dqc(1);
for(int i=2;i<=n;i++)
printf("%lld\n",c[i].f);
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: