【SPOJ】1825 Free tour II 点分治

2024-09-05 14:48
文章标签 ii free 分治 tour spoj 1825

本文主要是介绍【SPOJ】1825 Free tour II 点分治,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

传送门:【SPOJ】1825 Free tour II


题目分析:敲了两遍。。。

本题是论文题,具体见漆子超论文《分治算法在树的路径问题中的应用》。


在以root为根的第 i 棵子树上,我们用G[ i ,j ]表示root的第 i 棵子树的路径上严格有 j 个黑点的路径的最长长度。用F[ i ,j ]表示在root为根的第 i 棵子树的路径上不超过 j 个黑点的路径的最长长度。因为所有子树里包含黑点数最多的路径的包含黑点数X可以O(N)求出,我们按照每棵子树的X从小到大的顺序遍历,这样就能将G和F数组降低一维,以G[ i ]表示当前遍历的子树路径上严格有 i 个黑点的路径的最长长度,以F[ i ]表示在该子树之前所遍历的所有子树的路径上不超过 i 个黑点的路径的最长长度。

G[ i ]可以通过一次dfs求出,而F[ i ]可以在该子树遍历完以后用G[ i ]和F[ i ]的比较以及F[ i ]和F[ i - 1]的比较来更新。

而遍历顺序可以通过求出每个子树的X,然后把子树的遍历顺序按照X从小到大排序(用个结构体能很方便的解决)。

这样每层是大约O(NlogN)的复杂度,而最多只有logN层(参见点分治的复杂度),所以总复杂度大约为O(Nlog^2N)。


第一次AC了感觉掌握的还是不好,于是又敲了一遍,这才敢写文章。但是我感觉掌握的还是很不好,慢慢来,总有一天我会做的更好!



代码如下:


#include <cmath>
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std ;typedef long long LL ;#define travel( e , H , u ) for ( Edge* e = H[u] ; e ; e = e -> next )
#define rep( i , a , b ) for ( int i = ( a ) ; i <  ( b ) ; ++ i )
#define rev( i , a , b ) for ( int i = ( a ) ; i >= ( b ) ; -- i )
#define FOR( i , a , b ) for ( int i = ( a ) ; i <= ( b ) ; ++ i )
#define clr( a , x ) memset ( a , x , sizeof a )
#define cpy( a , x ) memcpy ( a , x , sizeof a )const int MAXN = 200005 ;
const int MAXE = 400005 ;
const int INF = 0x3f3f3f3f ;struct Edge {int v , c ;Edge* next ;
} E[MAXE] , *H[MAXN] , *edge ;struct Node {int v , c ;int num ;Node () {}Node ( int v , int num , int c ) : v ( v ) , num ( num ) , c ( c ) {}bool operator < ( const Node& a ) const {return num < a.num ;}
} T[MAXN] ;int siz[MAXN] ;
int num[MAXN] ;
int G[MAXN] ;
int F[MAXN] ;
bool vis[MAXN] ;
bool color[MAXN] ;
int root ;
int node_num ;
int n , m , K ;
int ans ;void clear () {ans = 0 ;edge = E ;num[0] = n ;clr ( H , 0 ) ;clr ( vis , 0 ) ;clr ( color , 0 ) ;
}void addedge ( int u , int v , int c ) {edge -> v = v ;edge -> c = c ;edge -> next = H[u] ;H[u] = edge ++ ;
}void get_size ( int u , int fa = 0 ) {siz[u] = 1 ;travel ( e , H , u ) {int v = e -> v ;if ( !vis[v] && v != fa ) {get_size ( v , u ) ;siz[u] += siz[v] ;}}
}void get_root ( int u , int fa = 0 ) {num[u] = 0 ;travel ( e , H , u ) {int v = e -> v ;if ( !vis[v] && v != fa ) {get_root ( v , u ) ;num[u] = max ( num[u] , siz[v] ) ;}}num[u] = max ( num[u] , node_num - siz[u] ) ;if ( num[u] < num[root] ) root = u ;
}void get_num ( int u , int fa = 0 ) {num[u] = color[u] ;travel ( e , H , u ) {int v = e -> v ;if ( !vis[v] && v != fa ) {get_num ( v , u ) ;num[u] = max ( num[u] , color[u] + num[v] ) ;}}
}void get_G ( int u , int fa , int dep , int val ) {G[dep] = max ( G[dep] , val ) ;travel ( e , H , u ) {int v = e -> v ;if ( !vis[v] && v != fa ) {get_G ( v , u , dep + color[v] , val + e -> c ) ;}}
}void dfs ( int u ) {get_size ( u ) ;//得到树的大小node_num = siz[u] ;root = 0 ;get_root ( u ) ;//求树的重心int rt = root , cnt = 0 ;vis[rt] = 1 ;//标记,将rt的所有子树分开travel ( e , H , rt ) if ( !vis[e -> v] ) dfs ( e -> v ) ;//递归求解travel ( e , H , rt ) {int v = e -> v ;if ( !vis[v] ) {get_num ( v ) ;//得到子树内节点数最多的路径的节点数T[cnt ++] = Node ( v , num[v] , e -> c ) ;}}sort ( T , T + cnt ) ;//修改访问顺序int limit = K - color[rt] ;FOR ( i , 0 , T[cnt - 1].num ) F[i] = -INF ;rep ( i , 0 , cnt ) {FOR ( j , 0 , T[i].num ) G[j] = -INF ;get_G ( T[i].v , rt , color[T[i].v] , T[i].c ) ;//dfs得到G[ ]if ( i ) {FOR ( j , 0 , T[i].num ) {if ( j > limit ) break ;int tmp = min ( T[i - 1].num , limit - j ) ;//与下面的更新有关,取之前已经更新过的if ( F[tmp] == -INF ) break ;ans = max ( ans , F[tmp] + G[j] ) ;}}FOR ( j , 0 , T[i].num ) {//更新到T[i].num,减小一点时间复杂度是一点if ( j > limit ) break ;F[j] = max ( F[j] , G[j] ) ;if ( j ) F[j] = max ( F[j] , F[j - 1] ) ;if ( j <= limit ) ans = max ( ans , F[j] ) ;}}vis[rt] = 0 ;//取消标记,将子树合并到大树上去
}void solve () {int x , y , c ;clear () ;while ( m -- ) {scanf ( "%d" , &x ) ;color[x] = 1 ;}rep ( i , 1 , n ) {scanf ( "%d%d%d" , &x , &y , &c ) ;addedge ( x , y , c ) ;addedge ( y , x , c ) ;}dfs ( 1 ) ;printf ( "%d\n" , ans ) ;
}int main () {while ( ~scanf ( "%d%d%d" , &n , &K , &m ) ) solve () ;return 0 ;
}


这篇关于【SPOJ】1825 Free tour II 点分治的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1139258

相关文章

Codeforces Round #240 (Div. 2) E分治算法探究1

Codeforces Round #240 (Div. 2) E  http://codeforces.com/contest/415/problem/E 2^n个数,每次操作将其分成2^q份,对于每一份内部的数进行翻转(逆序),每次操作完后输出操作后新序列的逆序对数。 图一:  划分子问题。 图二: 分而治之,=>  合并 。 图三: 回溯:

AI基础 L9 Local Search II 局部搜索

Local Beam search 对于当前的所有k个状态,生成它们的所有可能后继状态。 检查生成的后继状态中是否有任何状态是解决方案。 如果所有后继状态都不是解决方案,则从所有后继状态中选择k个最佳状态。 当达到预设的迭代次数或满足某个终止条件时,算法停止。 — Choose k successors randomly, biased towards good ones — Close

从0到1,AI我来了- (7)AI应用-ComfyUI-II(进阶)

上篇comfyUI 入门 ,了解了TA是个啥,这篇,我们通过ComfyUI 及其相关Lora 模型,生成一些更惊艳的图片。这篇主要了解这些内容:         1、哪里获取模型?         2、实践如何画一个美女?         3、附录:               1)相关SD(稳定扩散模型的组成部分)               2)模型放置目录(重要)

学习记录:js算法(二十八):删除排序链表中的重复元素、删除排序链表中的重复元素II

文章目录 删除排序链表中的重复元素我的思路解法一:循环解法二:递归 网上思路 删除排序链表中的重复元素 II我的思路网上思路 总结 删除排序链表中的重复元素 给定一个已排序的链表的头 head , 删除所有重复的元素,使每个元素只出现一次 。返回 已排序的链表 。 图一 图二 示例 1:(图一)输入:head = [1,1,2]输出:[1,2]示例 2:(图

LeetCode:3177. 求出最长好子序列 II 哈希表+动态规划实现n*k时间复杂度

3177. 求出最长好子序列 II 题目链接 题目描述 给你一个整数数组 nums 和一个非负整数k 。如果一个整数序列 seq 满足在下标范围 [0, seq.length - 2] 中 最多只有 k 个下标i满足 seq[i] != seq[i + 1] ,那么我们称这个整数序列为好序列。请你返回 nums中好子序列的最长长度。 实例1: 输入:nums = [1,2,1,1,3],

代码训练营 Day26 | 47.排序II | 51. N-皇后 |

47.排序II 1.跟46题一样只不过加一个树层去重 class Solution(object):def backtracking(self,nums,path,result,used):# recursion stopif len(path) == len(nums):# collect our setresult.append(path[:])return for i in range(

代码随想录训练营day37|52. 携带研究材料,518.零钱兑换II,377. 组合总和 Ⅳ,70. 爬楼梯

52. 携带研究材料 这是一个完全背包问题,就是每个物品可以无限放。 在一维滚动数组的时候规定了遍历顺序是要从后往前的,就是因为不能多次放物体。 所以这里能多次放物体只需要把遍历顺序改改就好了 # include<iostream># include<vector>using namespace std;int main(){int n,m;cin>>n>>m;std::vector<i

代码随想录刷题day25丨491.递增子序列 ,46.全排列 ,47.全排列 II

代码随想录刷题day25丨491.递增子序列 ,46.全排列 ,47.全排列 II 1.题目 1.1递增子序列 题目链接:491. 非递减子序列 - 力扣(LeetCode) 视频讲解:回溯算法精讲,树层去重与树枝去重 | LeetCode:491.递增子序列_哔哩哔哩_bilibili 文档讲解:https://programmercarl.com/0491.%E9%80%92%E

代码随想录算法训练营Day37|完全背包问题、518.零钱兑换II、377. 组合总和 Ⅳ、70. 爬楼梯(进阶版)

完全背包问题                  和01背包最大区别就是一个物品可以重复放多次,因此遍历空间时可以从前往后。 import java.util.*;public class Main{public static void main (String[] args) {Scanner sc = new Scanner(System.in);int m = sc.nextInt

HumanNeRF:Free-viewpoint Rendering of Moving People from Monocular Video 翻译

HumanNeRF:单目视频中运动人物的自由视点绘制 引言。我们介绍了一种自由视点渲染方法- HumanNeRF -它适用于一个给定的单眼视频ofa人类执行复杂的身体运动,例如,从YouTube的视频。我们的方法可以在任何帧暂停视频,并从任意新的摄像机视点或甚至针对该特定帧和身体姿势的完整360度摄像机路径渲染主体。这项任务特别具有挑战性,因为它需要合成身体的照片级真实感细节,如从输入视频中可能