BZOJ 3672 NOI2014 购票 树的分治 NOI2014全AC达成!!!!

27 篇文章 0 订阅
12 篇文章 0 订阅

警告 本篇文章作者大脑已成一团浆糊,为了保证文章的流畅性,请阅读者将脑子搅成纸浆后方可正常阅读

UPD:此题解已废,新题解戳这里

首先题目大意:

给定一棵以1为根的有根树,边有边权,每个点有三个参数:p,q,l

从该点可以走到它的祖宗节点处,前提是距离d不超过l且花销为pd+q

昨天时间不咋多,就没写。。。今天中午吃完饭开始写,结果一直写到五点半,一下午课都没去上,死定了0.0

这题如果不是数的话就是斜率优化 但是尼玛 这是棵树!

去网上搜了半天题解 写的基本都是树的分治 我还没写过0.0 就试着写了写

其实树的分治不难写 关键是那个该死的l。。。调了半天第三个点过不去,最后发现原来是这么挂的


问题就出在这种情况上,最优解不在凸包上,但是经过l的分割后l左侧的点无法选择,最优解就进入了凸包,这种情况没法维护。。。。

然后我回去重新看了下题解 尼玛 原来题解的意思是树分治+CDQ分治!我勒个去这代码复杂度明显不是我的单核小脑瓜能承受的0.0

最后我用暴力代替的CDQ,把l到得到的伪最优解之间的点一一枚举。。。然后惊奇地发现过了0.0 第四第五个点跑了2s多 还好没超时

最后贴代码0.0 写的有点惨 我慢 我慢!

#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#define M 200200
using namespace std;
typedef long long ll;
struct abcd{
	int to,next;
	ll f;
	int mark;
}table[M];
int head[M],tot;
void add(int x,int y,ll z)
{
	table[++tot].to=y;
	table[tot].f=z;
	table[tot].next=head[x];
	head[x]=tot;
}
int n,t,fa[M],siz[M],queue[M],mark[M],r,h,cnt;
ll p[M],q[M],l[M],f[M],dis[M];
typedef pair<ll,ll> point;
double getslope(point p1,point p2)
{
    if(p1.first==p2.first)
        return 2147483647*(p2.second>p1.second?1:-1);
    return (double)( p2.second-p1.second )/( p2.first-p1.first );
}
point ps[M];
struct Convex_Hull{
	point points[M];
    int num[M],top;
    double slope[M];
    void insert(point p,int pos)
    {
        double s=0;
        ll ff=p.second + ( dis[20] - p.first ) * ::p[20] + q[20];
		if(top)
        while(1)
        {
            s=getslope(points[top],p);
            if(top==1)
                break;
            if( s<slope[top] )
                top--;
            else
                break;
        }
        points[++top]=p;
        slope[top]=s;
        num[top]=pos;
    }
    int divide(ll d,double s)
    {
        int l,re;
        l=lower_bound(points+1,points+top+1, make_pair(d,0ll) )-points;
        if(l==top+1)
        	return -1;
        re=lower_bound(slope+l+1,slope+top+1,s)-slope;
        return re-1;
    }
}hull;
void CDQ_Tree(int root,int size,int T)
{
	int i,x,cog;
	
	if(size==1)
		return ;
	
	r=h=0;queue[++r]=root;
	while(r^h)
	{
		x=queue[++h];
		siz[x]=1;
		for(i=head[x];i;i=table[i].next)
			if(!table[i].mark)
				queue[++r]=table[i].to;
	}
	
	for(i=size;i;i--)
	{
		x=queue[i];
		siz[fa[x]]+=siz[x];
		if( (siz[x]<<1) > size )
			mark[fa[x]]=cnt;
		if( mark[x]!=cnt && (siz[x]<<1) >= size )
			cog=x;
	}
	
	for(i=head[cog];i;i=table[i].next)
		if(!table[i].mark)
			table[i].mark=T;
	CDQ_Tree(root,size-siz[cog]+1,++cnt);
	for(i=head[cog];i;i=table[i].next)
		if(table[i].mark==T)
			table[i].mark=0;

	r=0;
	for(i=cog;i!=fa[root];i=fa[i])
		queue[++r]=i;
	
	hull.top=0;h=r;
	hull.num[0]=r+1;
	for(i=r;i;i--)
		x=queue[i],ps[i]=make_pair(dis[x],f[x]),hull.insert( ps[i] , i );
	
	for(i=head[cog];i;i=table[i].next)
		queue[++r]=table[i].to;
	
	while(r^h)
	{
		x=queue[++h];
		int num=hull.divide( dis[x]-l[x] , (double)p[x] );
		if(num>=0)
			for(i=hull.num[num-1]-1;i>=hull.num[num];i--)
			{
				point p1=ps[i];
				if(dis[x]-ps[i].first<=l[x])
					f[x]=min( f[x] , p1.second + ( dis[x] - p1.first ) * p[x] + q[x] );
			}
		for(i=head[x];i;i=table[i].next)
			if(!table[i].mark)
				queue[++r]=table[i].to;
	}
	for(i=head[cog];i;i=table[i].next)
		CDQ_Tree(table[i].to,siz[table[i].to],++cnt);
}

int main()
{
	
	//freopen("ticket.in","r",stdin);
	//freopen("ticket.out","w",stdout);
	
	int i,x,y;
	ll dist;
	cin>>n>>t;
	for(i=2;i<=n;i++)
	{
		scanf("%d%lld%lld%lld%lld",&fa[i],&dist,&p[i],&q[i],&l[i]);
		add(fa[i],i,dist);
	}
	queue[++r]=1;
	while(r^h)
	{
		x=queue[++h];
		for(i=head[x];i;i=table[i].next)
			dis[table[i].to]=dis[x]+table[i].f,queue[++r]=table[i].to;
	}
	memset(f,0x3f,sizeof f);f[1]=0;
	CDQ_Tree(1,n,++cnt);
	for(i=2;i<=n;i++)
		printf("%lld\n",f[i]==4557430888798830399ll?0:f[i]);
}


  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值