zoukankan      html  css  js  c++  java
  • bzoj 3784: 树上的路径【点分治+st表+堆】

    参考:https://www.cnblogs.com/CQzhangyu/p/7071477.html
    神奇的点分治序(或者叫点剖?)。就是把点分治扫过的点依次放进队列里,然后发现,对于每一棵树摊到序列上,每个点的值v是重心到这个点的距离,那么对序列上的每个点定义l为这个子树重心在序列上的位置,r为在这个重心下的当前扫的子树的前一棵被扫过的子树(天啊我在说什么),所以当前点的v+当前点的(l,r)中最大的v值就是以当前的重心为转折点接起来的两条路径,因为是r前一棵子树不会重复也不会出现计算两边同一条边的情况。
    那么问题就变成了对于一个序列,前m大的“一个点值+这个点(l,r)中的最大值”。做法同bzoj 2006.

    #include<iostream>
    #include<cstdio>
    #include<queue>
    using namespace std;
    const int N=1000005;
    int n,m,cnt,root,mx,tot,nm,h[N],si[N],v[N],lp[N],rp[N],b[N],st[N][25];
    bool vis[N];
    struct qw
    {
    	int ne,to,va;
    }e[N];
    struct qwe
    {
    	int i,l,r,v,p;
    	bool operator < (const qwe &a) const
    	{
    		return v<a.v;
    	}
    };
    priority_queue<qwe>q;
    int read()
    {
    	int r=0,f=1;
    	char p=getchar();
    	while(p>'9'||p<'0')
    	{
    		if(p=='-')
    			f=-1;
    		p=getchar();
    	}
    	while(p>='0'&&p<='9')
    	{
    		r=r*10+p-48;
    		p=getchar();
    	}
    	return r*f;
    }
    int Max(int a,int b)
    {
    	return v[a]>v[b]?a:b;
    }
    void ins(int i,int l,int r)
    {
    	qwe now;
    	now.i=i,now.l=l,now.r=min(r,nm);
    	if(now.l>now.r)
    		return;
    	int k=b[now.r-now.l+1];
    	now.p=Max(st[now.l][k],st[now.r-(1<<k)+1][k]);
    	now.v=v[now.p]+v[now.i];//cout<<now.i<<" "<<now.l<<" "<<now.r<<" "<<now.p<<" "<<now.v<<endl;
    	q.push(now);
    }
    void add(int u,int v,int w)
    {
    	cnt++;
    	e[cnt].ne=h[u];
    	e[cnt].to=v;
    	e[cnt].va=w;
    	h[u]=cnt;
    }
    void getroot(int u,int fa)
    {
    	si[u]=1;
    	int mxx=0;
    	for(int i=h[u];i;i=e[i].ne)
    		if(!vis[e[i].to]&&e[i].to!=fa)
    		{
    			getroot(e[i].to,u);
    			si[u]+=si[e[i].to];
    			mxx=max(mxx,si[e[i].to]);
    		}
    	if(mx>max(mxx,tot-si[u]))
    	{
    		root=u;
    		mx=max(mxx,tot-si[u]);
    	}
    }
    void getdeep(int u,int fa,int de)
    {
    	v[++nm]=de;
    	lp[nm]=lp[nm-1];
    	rp[nm]=rp[nm]?rp[nm]:rp[nm-1];
    	for(int i=h[u];i;i=e[i].ne)
    		if(e[i].to!=fa&&!vis[e[i].to])
    			getdeep(e[i].to,u,de+e[i].va);
    }
    void dfs(int u)
    {
    	vis[u]=1;
    	v[++nm]=0,lp[nm]=nm,rp[nm]=nm-1;
    	for(int i=h[u];i;i=e[i].ne)
    		if(!vis[e[i].to])
    		{
    			rp[nm+1]=nm;
    			getdeep(e[i].to,u,e[i].va);
    		}
    	for(int i=h[u];i;i=e[i].ne)
    		if(!vis[e[i].to])
    		{
    			tot=si[e[i].to];
    			mx=1<<30;
    			getroot(e[i].to,u);
    			dfs(root);
    		}
    }
    int main()
    {
    	n=read(),m=read();
    	for(int i=1;i<n;i++)
    	{
    		int x=read(),y=read(),z=read();
    		add(x,y,z);
    		add(y,x,z);
    	}
    	tot=n,mx=1<<30;
    	getroot(1,0);
    	dfs(root);//cout<<"ok"<<endl;
    	// for(int i=1;i<=nm;i++)
    		// cout<<v[i]<<" ";
    	// cout<<endl;
    	b[1]=0;
    	for(int i=2;i<=nm;i++)
    		b[i]=b[i>>1]+1;
    	for(int i=1;i<=nm;i++)
    		st[i][0]=i;
    	for(int j=1;(1<<j)<=nm;j++)
            for(int i=1;i+(1<<j)-1<=nm;i++)
                st[i][j]=Max(st[i][j-1],st[i+(1<<j-1)][j-1]);
    	for(int i=1;i<=nm;i++)
    		ins(i,lp[i],rp[i]);
    	for(int i=1;i<=m;i++)
    	{
    		qwe now=q.top();
    		q.pop();//cout<<now.i<<" "<<now.l<<" "<<now.r<<" "<<now.p<<" "<<now.v<<endl;
    		printf("%d
    ",now.v);
    		ins(now.i,now.l,now.p-1);
    		ins(now.i,now.p+1,now.r);
    	}
    	return 0;
    }
    
  • 相关阅读:
    2018年左其盛读过评过的书(持续更新中)
    2星|《用场景营销引爆你的生意》:总共4个推荐案例,3个已经失败
    2018左其盛经管新书差评榜(持续更新中)
    3星|《十大全球CEO亲授企业高速成长的关键战略》:作为CEO,我也非常坦率地表明过家庭优先于工作
    2018左其盛好书榜(持续更新中)
    3星|《你的品牌需要一个讲故事的人》:有理论没案例
    《思考快与慢》前传,两位天才犹太心理学家的传奇人生与学术故事:4星|《思维的发现》
    C#如何在派生类中不显示父类的一些属性以及TypeDescriptor使用
    在XML里的XSD和DTD以及standalone的使用
    数据库操作之简单带参操作
  • 原文地址:https://www.cnblogs.com/lokiii/p/8412789.html
Copyright © 2011-2022 走看看