BZOJ1776 - [Usaco2010 Hol]cowpol 奶牛政坛

题目链接:http://www.lydsy.com/JudgeOnline/problem.php?id=1776

题目大意:一棵n个点的树,树上每个点属于一个党派,要求每个党派的最远距离点。两点间距离为两点间边的个数。

主观思路:看到树上两点距离立即想到dep[x] + dep[y] - 2 * dep[lca(x, y)]这件事。于是想到树形序列化的思想,搞成一个中序遍历的dfs序列。序列中每个点有一个dep,也有一个col表示点深度和所属党派,依次枚举dfs序列中的点,对于当前点x它与之前所有同党派y之间的最远距离取决于dep[y] - 2 * min{dep[k], k在dfs序列y,x之间}。这里用了一种类似单调队列的思想可以做到每个点只进队(我对每个党派的点开了一个vector)一次,详见代码,属于乱搞。对于那个min就用rmq提前对dep维护一下即可。

  1 /**************************************************************
  2     Problem: 1776
  3     User: SWUN2015
  4     Language: C++
  5     Result: Accepted
  6     Time:1988 ms
  7     Memory:48060 kb
  8 ****************************************************************/
  9  
 10 #include <cstdio>
 11 #include <cstdlib>
 12 #include <cstring>
 13 #include <cmath>
 14 #include <vector>
 15 #include <algorithm>
 16 using namespace std;
 17   
 18 const int MaxN = 2 * 1e5, MaxM = 4 * 1e5;
 19 struct ARR
 20 {
 21     int loc, dep, MiN, pre;
 22 };
 23 int n, K, all, Root, tot;
 24 int dep[MaxN + 5], col[MaxN + 5], dfs_seq[MaxN + 5], ans[MaxN + 5];
 25 int p[MaxN + 5], pre[MaxM + 5], last[MaxN + 5], other[MaxM + 5];
 26 int rmq[MaxN + 5][30];
 27 vector <ARR> a[MaxN + 5];
 28   
 29 void Build(int x, int y)
 30 {
 31     pre[++all] = last[x];
 32     last[x] = all;
 33     other[all] = y;
 34 }
 35   
 36 void Init()
 37 {
 38     all = -1; memset(last, -1, sizeof(last));
 39     scanf("%d%d", &n, &K);
 40     for (int i = 1; i <= n; i++)
 41     {
 42         scanf("%d%d", &col[i], &p[i]);
 43         Build(i, p[i]); Build(p[i], i);
 44         if (p[i] == 0) Root = i;
 45     }
 46 }
 47   
 48 void Dfs(int x, int fa, int d)
 49 {
 50     dfs_seq[++tot] = x;
 51     dep[x] = d;
 52     int ed = last[x], dr;
 53     while (ed != -1)
 54     {
 55         dr = other[ed];
 56         if (dr != fa) Dfs(dr, x, d + 1);
 57         ed = pre[ed];
 58     }
 59 }
 60   
 61 void RMQ()
 62 {
 63     int K = (int)(double(log(n)) / double(log(2)));
 64     for (int i = 1; i <= n; i++) rmq[i][0] = dep[dfs_seq[i]];
 65     for (int i = 1; i <= K; i++)
 66         for (int j = 1; j <= n - (1 << i) + 1; j++)
 67             rmq[j][i] = min(rmq[j][i - 1], rmq[j + (1 << (i - 1))][i - 1]);
 68 }
 69   
 70 void Insert(int x)
 71 {
 72     int t, c = col[dfs_seq[x]]; 
 73     if (a[c].size() > 0)
 74     {
 75         t = a[c].size();
 76         int pos, dd = 0;
 77         int l = a[c][--t].loc + 1, r = x;
 78         int K = (int)(double(log(r - l + 1)) / double(log(2)));
 79         int MiN = min(rmq[l][K], rmq[r - (1 << K) + 1][K]);
 80         ARR res;
 81         while (t >= 0 && MiN <= a[c][t].MiN) 
 82         {
 83             if (a[c][t].dep >= dd) dd = a[c][t].dep, pos = a[c][t].loc;
 84             a[c].pop_back(); t--;
 85         }   
 86         res.dep = dd; res.loc = pos; res.MiN = MiN; 
 87         if (t >= 0) res.pre = max(a[c][t].pre, res.dep - 2 * (res.MiN - 1));
 88             else res.pre = res.dep - 2 * (res.MiN - 1);
 89         a[c].push_back(res);
 90         res.dep = dep[dfs_seq[x]]; res.loc = x; res.MiN = 1 << 30; 
 91         res.pre = a[c][t + 1].pre;
 92         a[c].push_back(res);
 93         t += 2;
 94         ans[c] = max(ans[c], a[c][t].pre + dep[dfs_seq[x]]);
 95     }
 96     else
 97     {
 98         t = 0;
 99         ARR res;
100         res.dep = dep[dfs_seq[x]]; res.loc = x; res.MiN = 1 << 30; res.pre = -(1 << 30);
101         a[c].push_back(res);
102     }
103 }
104   
105 void Solve()
106 {
107     Dfs(Root, 0, 0); RMQ();
108     for (int i = 1; i <= n; i++) {Insert(i);}//printf("%d %d\n", dfs_seq[i], ans[col[dfs_seq[i]]]);}
109     for (int i = 1; i <= K; i++) printf("%d\n", ans[i]);
110 }
111   
112 int main()
113 {
114     Init();
115     Solve();
116 }
View Code

于是这道题就成了RMQ预处理之后的O(N)乱搞题,然而看了题解之后异常崩溃。

有这样一个结论:每个政党中的最远距离点对,必然有一个点是该政党dep最深的点。仔细考虑没有理由反驳…

于是题目变得很水,找到每个党派的dep最深点,再顺次枚举该党派的所有点,找到lca求值更新答案即可。复杂度O(nlogn)。

转载于:https://www.cnblogs.com/ChopsticksAN/p/5137899.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值