zoukankan      html  css  js  c++  java
  • [bzoj2599][IOI2011]Race_树上点分治

    Race bzoj-2599

        题目大意:询问一颗树上最短的、长度为k的链,边有边权,n个节点。

        注释:$1le n le 2cdot 10^5$,$1le k le 10^6$。

          想法:树上点分治的另一种表现方式。首先,由于题目中要求的是最小值,我们发现这东西可加不可减。不可减意味着什么?意味着我们递归计算当前树时无法将它的单个子树的情况减掉。所以之前的单步容斥的算法就收到了打压qwq。我们思考另一种方法。首先,类似于dfs的,我一定是对于当前root一颗子树一颗子树地递归,只有当前子树的信息已经完全处理好的情况下我才会去处理下一颗子树。这就相当于我在处理当前子树的时候之前的子树已经是完善的了。所以,我可以开一个桶,记录长度为i的链的最短长度,然后对于当前子树我可以直接调用桶中信息,即可。然后,还原桶的操作是简单的,就是说如果当前桶所代表的边权小于等于k,那么久有可能是被更新过的,将其还原即可。

        最后,附上丑陋的代码... ...

    #include <iostream>
    #include <cstdio>
    #include <cstring>
    #include <algorithm>
    #define N 200010 
    using namespace std;
    const int inf=1<<30;
    int to[2*N],nxt[2*N],head[N],val[N*2],tot;
    int size[N],vis[N],v[N*5],f[N],dis[N],deep[N];
    //dis数组表示当前节点到根节点之间的边权和
    //deep数组表示当前节点到根节点路径深度,即链长度
    //vis数组表示当前节点是否已经被删除(当过根节点)
    //v数组是桶
    int ms,root;
    int n,k;
    int ans=inf;
    inline void add(int x,int y,int z)//加边
    {
    	to[++tot]=y;
    	val[tot]=z;
    	nxt[tot]=head[x];
    	head[x]=tot;
    }
    void getroot(int pos,int fa)//找重心、处理size
    {
    	size[pos]=1;
    	f[pos]=0;
    	for(int i=head[pos];i;i=nxt[i])
    	{
    		if(to[i]==fa||vis[to[i]]) continue;
    		getroot(to[i],pos);
    		size[pos]+=size[to[i]];
    		f[pos]=max(size[to[i]],f[pos]);
    	}
    	f[pos]=max(f[pos],ms-size[pos]);
    	if(f[root]>f[pos]) root=pos;
    	// puts("getroot");
    }
    void clear(int pos,int fa)//还原桶
    {
    	if(dis[pos]<=k) v[dis[pos]]=inf;
    	for(int i=head[pos];i;i=nxt[i])
    	{
    		if(to[i]==fa||vis[to[i]]) continue;
    		clear(to[i],pos);
    	}
    	// puts("clear");
    }
    void insert(int pos,int fa)//修改桶
    {
    	if(dis[pos]<=k)
    	{
    		v[dis[pos]]=min(v[dis[pos]],deep[pos]);
    	}
    	for(int i=head[pos];i;i=nxt[i])
    	{
    		if(vis[to[i]]||to[i]==fa) continue;
    		insert(to[i],pos);
    		// puts("insert");
    	}
    }
    void calc(int pos,int fa)//计算答案
    {
    	if(dis[pos]<=k)
    	{
    		ans=min(ans,deep[pos]+v[k-dis[pos]]);
    	}
    	for(int i=head[pos];i;i=nxt[i])
    	{
    		if(vis[to[i]]||to[i]==fa) continue;
    		deep[to[i]]=deep[pos]+1;
    		dis[to[i]]=dis[pos]+val[i];
    		calc(to[i],pos);
    	}
    	// puts("calc");
    }
    void solve(int pos)//点分治过程
    {
    	vis[pos]=true;
    	// deep[pos]=0;
    	v[0]=0;
    	for(int i=head[pos];i;i=nxt[i])
    	{
    		if(vis[to[i]]) continue;
    		deep[to[i]]=1;
    		dis[to[i]]=val[i];
    		calc(to[i],0);
    		insert(to[i],0);
    	}
    	for(int i=head[pos];i;i=nxt[i])
    	{
    		if(vis[to[i]]) continue;
    		clear(to[i],0);
    	}
    	for(int i=head[pos];i;i=nxt[i])
    	{
    		if(vis[to[i]]) continue;
    		ms=size[to[i]];
    		root=0;
    		getroot(to[i],0);
    		solve(root);
    	}
    	// puts("solve");
    }
    int main()
    {
    	// int n,k;
    	scanf("%d%d",&n,&k);
    	for(int x,y,z,i=1;i<n;i++)
    	{
    		scanf("%d%d%d",&x,&y,&z);
    		add(x+1,y+1,z);//点编号是从0开始的
    		add(y+1,x+1,z);
    	}
    	// v[0]=0;
    	for(int i=1;i<=k;i++)
    	{
    		v[i]=inf;
    	}
    	root=0;
    	f[0]=n;
    	// dfs(1);
    	ms=n;
    	getroot(1,0);
    	// printf("%d
    ",root);
    	solve(root);
    	if(ans==inf) printf("-1
    ");
    	else printf("%d
    ",ans);
    	return 0;
    }
    

        小结:一定要注意dis数组和deep数组分别的含义。然后主函数里所有函数的fa都是0,因为是递归处理,但是其实将fa改成pos也没有问题,因为pos节点已经当过重心、被删除了。

  • 相关阅读:
    进程池,线程池,协程,gevent模块,协程实现单线程服务端与多线程客户端通信,IO模型
    线程相关 GIL queue event 死锁与递归锁 信号量l
    生产者消费者模型 线程相关
    进程的开启方式 进程的join方法 进程间的内存隔离 其他相关方法 守护进程 互斥锁
    udp协议 及相关 利用tcp上传文件 socketserver服务
    socket套接字 tcp协议下的粘包处理
    常用模块的完善 random shutil shevle 三流 logging
    day 29 元类
    Django入门
    MySQL多表查询
  • 原文地址:https://www.cnblogs.com/ShuraK/p/8829642.html
Copyright © 2011-2022 走看看