• 点分治


    前言

    WARNING:代码有点问题!但是懒得改了!

    好久没见过这么优雅的暴力了,这不学后悔一生啊!

    说实话,还不太会。

    讲解

    其实就是改变一下树的遍历顺序,然后做到 (O(nlogn)) 的时间复杂度

    大概的思路是:

    对于一棵树,我们找到它的重心,以其为根向下延伸,然后对于它的子树我们也做这样的操作,找到它们的重心作为其新的儿子。由于我们改变了遍历顺序,所以我们要用 (vis) 数组将已经经过的点打上标记。

    经典操作是对于每个点,我们可以遍历其整棵子树,然后用一些操作更新答案,通常我们见到的操作可以用 数据结构 或者 two pointers 更新。这样的时间复杂度是正确的 (O(nlogn)),多么优雅的暴力,不是吗。

    具体的可以看板题代码。

    练习

    板题(洛谷)

    代码

    板题代码。

    int head[MAXN],tot;
    struct edge
    {
    	int v,w,nxt;
    }e[MAXN << 1];
    void Add_Edge(int x,int y,int z)
    {
    	e[++tot].v = y;
    	e[tot].w = z;
    	e[tot].nxt = head[x];
    	head[x] = tot;
    }
    void Add_Double_Edge(int x,int y,int z)
    {
    	Add_Edge(x,y,z);
    	Add_Edge(y,x,z);
    }
    
    bool vis[MAXN];
    int siz[MAXN],MAX[MAXN];
    void getrt(int x,int fa,int S)//找重心
    {
    	siz[x] = 1;
    	MAX[x] = 0;
    	for(int i = head[x]; i ;i = e[i].nxt)
    	{
    		if(e[i].v == fa || vis[e[i].v]) continue;
    		getrt(e[i].v,x,S);
    		siz[x] += siz[e[i].v];
    		MAX[x] = Max(MAX[x],siz[e[i].v]);
    	}
    	MAX[x] = Max(MAX[x],S-siz[x]);
    	if(!rt || MAX[x] < MAX[rt]) rt = x; 
    }
    int cnt;
    int p[MAXN],bl[MAXN],dis[MAXN];
    void getdis(int x,int fa,int D,int from) 
    {
    	p[++cnt] = x;
    	bl[x] = from;
    	dis[x] = D;
    	for(int i = head[x]; i ;i = e[i].nxt)
    		if(e[i].v != fa && !vis[e[i].v])
    			getdis(e[i].v,x,D+e[i].w,from);
    }
    bool cmp(int x,int y){return dis[x] < dis[y];}
    void update(int x)
    {
    	cnt = 0;
    	p[++cnt] = x;
    	bl[x] = x;
    	dis[x] = 0;
    	for(int i = head[x]; i ;i = e[i].nxt)
    		if(!vis[e[i].v]) getdis(e[i].v,x,e[i].w,e[i].v);
    	sort(p+1,p+cnt+1,cmp);
    	for(int i = 1;i <= Q;++ i)
    	{
    		if(aye[i]) continue;
    		int l = 1,r = cnt;
    		while(l < r)
    		{
    			if(dis[p[l]] + dis[p[r]] < q[i]) l++;
    			else if(dis[p[l]] + dis[p[r]] > q[i]) r--;
    			else if(bl[p[l]] == bl[p[r]]) 
    			{
    				if(bl[p[r]] == bl[p[r-1]]) r--;
    				else l++;
    			}
    			else 
    			{
    				aye[i] = 1;
    				break;
    			}
    		}
    	}
    }
    void solve(int x)
    {
    	vis[x] = 1;
            //应该在这个位置重新跑一遍dfs更新siz
    	update(x);
    	for(int i = head[x]; i ;i = e[i].nxt)
    	{
    		if(vis[e[i].v]) continue;
    		rt = 0;//记得初始化!
    		getrt(e[i].v,x,siz[e[i].v]);//这里的siz没更新,有点问题
    		solve(rt);
    	}
    }
    
    int main()
    {
    //	freopen(".in","r",stdin);
    //	freopen(".out","w",stdout);
    	n = Read(); Q = Read();
    	for(int i = 1,u,v;i < n;++ i) u = Read(),v = Read(),Add_Double_Edge(u,v,Read());
    	for(int i = 1;i <= Q;++ i)
    	{
    		q[i] = Read();
    		if(!q[i]) aye[i] = 1;
    	}
    	getrt(1,0,n);
    	solve(rt);
    	for(int i = 1;i <= Q;++ i)
    	{
    		if(aye[i]) printf("AYE
    ");
    		else printf("NAY
    ");
    	}
    	return 0;
    }
    
  • 相关阅读:
    Java笔记十:Java数组
    Java笔记九:Java内置的包装类(2)
    2019-2020-1 20199316《Linux内核原理与分析》第二周作业
    2019-2020-1 20199316《Linux内核原理与分析》第一周作业
    2019-2020-1 20199312《Linux内核原理与分析》第十二周作业
    2019-2020-1 20199312《Linux内核原理与分析》第十一周作业
    2019-2020-1 20199312 《Linux内核原理与分析》 第九周作业
    要求用Windows下批处理和Linux下的shell脚本完成,两文本交替输出
    2019-2020-1 20199312 《Linux内核原理与分析》 第八周作业
    wait,waitpid学习测试
  • 原文地址:https://www.cnblogs.com/PPLPPL/p/14407345.html
Copyright © 2020-2023  润新知