Free tour II SPOJ - FTOUR2 && 点分治

博客介绍了如何利用点分治和启发式合并策略解决SPOJ上的FTOUR2问题。在给定一棵树和限制条件(经过的黑色点数不超过K)下,寻找路径长度最大值。博主经历了从二分到线段树,再到启发式合并的思路转变,最终实现nlogn的时间复杂度解法。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题目大意

给你一棵树,树上的节点有白点有黑点,其中找到一条路径使得在经过的黑色点数不超过K的时候 路径长度最大,输出这个最大值

input

8 2 3 (n, k, m)
3
5
7 (m个黑点)
1 3 1
2 3 10
3 4 -2
4 5 -1
5 7 6
5 6 5
4 8 3 (边的连接情况以及权值)

output

12

idea

我做这道题做了好久啊。
这道题的核心就是怎么维护一条路上黑点个数在0~x范围内的路径最大值。我一开始没有想好,用的二分,后来wa了,仔细想了想,这个维护不了。又用了线段树。查询只需要logn,但是递归完一个子树的时候需要把线段树重新初始化,这样复杂度又变成了n2。
看了别人的代码,当时做这个题的时候也是看了很久很久,看得自己也是稀里糊涂的,昨天晚上整理,去看了一下启发式合并,虽然那个题没看懂,但是听他们一说,大约明白了启发式合并的意思,就是把小的集合往大的集合合并。
比如[1, 2], [1,2,3,4,5,6,7…1e9] 我们要合并这两个集合,如果把第二个集合合并到第一个集合,复杂度很大,所以要把第一个集合合并到第二个集合。
这里就很巧妙地用到了这个思想。
当我们跑到以root为根的另一个子树的时候,对于正在跑的该点,需要找已经跑过的子树符合条件的并且路径最大的(比如黑点的个数不能超过10,跑到当前点黑点的个数已经有3个了,我们就要去之前跑过的子树里找黑点个数在0~7范围内的路径的最大值),然后想了很多办法发现都不行,只能暴力去跑。然后就知道了要用启发式合并。先把每棵子树的黑点个数的最大值跑出来,排个序,每次用在黑点个数少的里面去找合法的。
然后时间复杂度是nlogn

code

#include <bits/stdc++.h>
using namespace std;

#define lson Root << 1
#define rson Root << 1 | 1
#define MID int mid = (l + r) / 2
const int maxn = 500000;
const int INF = 0x3f3f3f3f;

struct node {
   int v, w;
};

int k, allNode, sonMax[maxn], sonNum[maxn], ans, root, n;
bool vis[maxn], black[maxn];
vector<node> Edge[maxn];


void getroot(int now, int pre) {
   sonNum[now] = 1;
   sonMax[now] = 0;
   for(int i = 0; i < Edge[now].size(); i++) {
      int to = Edge[now][i].v;
      if(to == pre || vis[to]) continue;
      getroot(to, now);
      sonNum[now] += sonNum[to];
      sonMax[now] = max(sonMax[now], sonNum[to]);
   }
   sonMax[now] = max(sonMax[now], allNode - sonNum[now]);
   if(sonMax[now] < sonMax[root]) root = now;
}

int blackNum[maxn], dist[maxn], Max_Black;
//blackNum[i] 是i点的黑点个数
//dist[i] 是i点到根的距离
//Max_Black 是该子树黑点的最大值

void Get_Max_Black(int now, int pre) { //求该子树黑点的最大值
   Max_Black = max(Max_Black, blackNum[now]);
   for(int i = 0; i < Edge[now].size(); i++) {
      int to = Edge[now][i].v;
      if(vis[to] || to == pre) continue;
      blackNum[to] = blackNum[now] + black[to];
      dist[to] = dist[now] + Edge[now][i].w;
      Get_Max_Black(to, now);
   }
}

vector<pair<int, int> >sonTree; //first存黑点数最大值,second存是哪颗子树
int Max_dist[maxn]; //Max_dist[i] 是黑点数为i的距离最大值

void Get_dist(int now, int pre) { //得到Max_dist数组
   int num = blackNum[now];
   Max_dist[num] = max(Max_dist[num], dist[now]);

   for(int i = 0; i < Edge[now].size(); i++) {
     int to = Edge[now][i].v;
     if(vis[to] || to == pre) continue;
     Get_dist(to, now);
   }
}

int Max[maxn];

void Deal_son_tree(int now) {
   sonTree.clear();
   for(int i = 0; i < Edge[now].size(); i++) {
      int to = Edge[now][i].v;
      if(vis[to]) continue;
      Max_Black = 0;
      dist[to] = Edge[now][i].w;
      blackNum[to] = black[to];
      Get_Max_Black(to, now); //求该子树黑点的最大值
      sonTree.push_back(make_pair(Max_Black, to));
   }

   sort(sonTree.begin(), sonTree.end());

   for(int i = 0; i < sonTree.size(); i++) {  //启发式合并
       Get_dist(sonTree[i].second, now);
       int num = 0;
       if(i) {
           for(int j = sonTree[i].first; j >= 0; j--) {
              while(num + j < k && num < sonTree[i - 1].first) {
                  num++;
                  Max[num] = max(Max[num], Max[num - 1]);
              }
              if(j + num <= k) ans = max(ans, Max[num] + Max_dist[j]);
          }
       }
       if(i == sonTree.size() - 1) {
          for(int j = 0; j <= sonTree[i].first; j++) {
              if(j <= k) ans = max(max(Max[j], Max_dist[j]), ans);
              Max[j] = Max_dist[j] = 0;
          }
       }
       else {
          for(int j = 0; j <= sonTree[i].first; j++) {
             Max[j] = max(Max[j], Max_dist[j]);
             Max_dist[j] = 0;
          }
       }
   }
}

void solve(int now) {
   vis[now] = true;
   if(black[now]) k--;
   Deal_son_tree(now);
   if(black[now]) k++;

   for(int i = 0; i < Edge[now].size(); i++) {
      int to = Edge[now][i].v;
      if(vis[to]) continue;
      sonMax[0] = allNode = sonNum[to];
      getroot(to, root = 0);
      solve(root);
   }
}

int main()
{
    int m;
    scanf("%d %d %d", &n, &k, &m);
    for(int i = 0; i < m; i++) {
        int u;
        scanf("%d", &u);
        black[u] = true;
    }
    for(int i = 1; i < n; i++) {
        int u, v, w;
        scanf("%d %d %d", &u, &v, &w);
        Edge[u].push_back((node){v, w});
        Edge[v].push_back((node){u, w});
    }

    ans = 0;
    sonMax[0] = allNode = n;
    getroot(1, root = 0);
    solve(root);
    printf("%d\n", ans);
}
内容概要:《2024年中国城市低空经济发展指数报告》由36氪研究院发布,指出低空经济作为新质生产力的代表,已成为中国经济新的增长点。报告从发展环境、资金投入、创新能力、基础支撑和发展成效五个维度构建了综合指数评价体系,评估了全国重点城市的低空经济发展状况。北京和深圳在总指数中名列前茅,分别以91.26和84.53的得分领先,展现出强大的资金投入、创新能力和基础支撑。低空经济主要涉及无人机、eVTOL(电动垂直起降飞行器)和直升机等产品,广泛应用于农业、物流、交通、应急救援等领域。政策支持、市场需求和技术进步共同推动了低空经济的快速发展,预计到2026年市场规模将突破万亿元。 适用人群:对低空经济发展感兴趣的政策制定者、投资者、企业和研究人员。 使用场景及目标:①了解低空经济的定义、分类和发展驱动力;②掌握低空经济的主要应用场景和市场规模预测;③评估各城市在低空经济发展中的表现和潜力;④为政策制定、投资决策和企业发展提供参考依据。 其他说明:报告强调了政策监管、产业生态建设和区域融合错位的重要性,提出了加强法律法规建设、人才储备和基础设施建设等建议。低空经济正加速向网络化、智能化、规模化和集聚化方向发展,各地应找准自身比较优势,实现差异化发展。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值