zoukankan      html  css  js  c++  java
  • CF1266F [*medium]

    看到其他题解里面全是什么 bfs 序上线段树啊,什么根号的奇怪东西啊,蒟蒻用了一个非常好写的 (O(n)) 做法(这里实现的时候用了 vector,所以比较慢),写篇题解来造福社会

    目前在 cf 上是最短解


    如果 (k = 1),答案是 (max(dep_i + 1))

    考虑有 (3) 个点的情况:

    通过放缩法可以证明(证明比较简单而繁琐略去)。考虑在这种情况下的链长(图中的 (a, b, c)):

    1. (k) 为奇数:有一个中心结点,旁边的链最多只有一个长为 (frac{k-1}{2}) 的,其他都是 (frac{k + 1}{2})
    2. (k) 为偶数:有一个中心结点,旁边的都是长为 (frac{k}{2}) 的链。

    但是这样会发现在样例 (2) 挂掉了。漏掉了下面的情况:

    因此有了第 (3) 种情况 :在 (k) 为偶数的时候,有两个中心结点,旁边链长要求为 (frac{k}{2})

    在这 (3) 种情况下,我们发现一定满足 (ans_x ge ans_{x + 2})

    对于前两个情况,每一个结点我们记录以他为根时的子树深度,然后把这个深度进行排序。(k) 为偶数时第 (i) 大数 (t) 的则表示 (ans_{2t} ge i) 。奇数稍微麻烦点,第 (i) 大数 (t) 的则表示 (ans_{2t-1} ge i),如果不和排在前面的数相同,那么我们发现长度为 (ans_{2t+1} ge i)(用这条链和之前面的几条链放在一起,就是长度为 (t) 和一堆 (t + 1))。这里都很显然。

    (3) 种情况,显然可以把所有相邻的两个位置 (A), (B) 的子树深度数组给合并在一起,然后再按照第一种情况做就行了,可惜是 (n^2) 的。

    考虑从后到前,对于这个子树深度数组扫描线,扫到 (k) 时更改每一个数的时候判一下和相邻结点的点的和 (sum - 2)(sum) 会算到 (B)(A) 子树和 (A)(B) 子树的贡献,因此 (sum) 要减 (2))是否可以更新 (ans_{k})

    这个可以套路地看作是计算父亲结点和子树结点的最大值,额外记录一下子树 (sum) 的最大值 (mxs),在修改一个结点的时候更新父亲结点的 (mxs),同时用 (max(sum_{fa}, mxs)) 来更新答案。

    具体实现时,第 (1) 和第 (2) 种情况也可以把排序换成扫描线做到 (O(n))

    其余细节见代码。

    #include<bits/stdc++.h>
    #define L(i, j, k) for(int i = j, i##E = k; i <= i##E; i++)
    #define R(i, j, k) for(int i = j, i##E = k; i >= i##E; i--)
    using namespace std;
    const int N = 5e5 + 7;
    void Max(int &x, int y) { if(x < y) x = y;  }
    int n, deg[N], Fa[N], f1[N], f2[N], sum[N], mx1[N], mx2[N], up[N], mxs[N], las, u, v;
    vector<int> G[N], e[N];
    void dfs1(int x) {
    	Max(f1[0], deg[x] + 1), mxs[x] = -1e9;
    	for(int v : e[x]) if(v ^ Fa[x]) {
    		Fa[v] = x, dfs1(v);
    		if(mx1[v] + 1 > mx1[x]) mx2[x] = mx1[x], mx1[x] = mx1[v] + 1; else Max(mx2[x], mx1[v] + 1);
    	}
    }
    void dfs2(int x) {
    	if(x ^ 1) G[up[x]].push_back(x);
    	for(int v : e[x]) if(v ^ Fa[x]) G[mx1[v] + 1].push_back(x);
    	for(int v : e[x]) if(v ^ Fa[x]) up[v] = max(up[x], mx1[v] + 1 == mx1[x] ? mx2[x] : mx1[x]) + 1, dfs2(v);
    }
    int main() {
    	ios::sync_with_stdio(false);
    	cin.tie(0), cout.tie(0);
    	cin >> n, f1[n] = f2[n] = 1;
    	L(i, 1, n - 1)  cin >> u >> v, deg[u] ++, deg[v] ++, e[u].push_back(v), e[v].push_back(u);
    	dfs1(1), dfs2(1);
    	R(i, n, 1) {
    		las = 0;
    		for(int t : G[i]) {
    			sum[t] ++, Max(mxs[Fa[t]], sum[t]);
    			Max(f2[i], sum[t] + mxs[t] - 2), Max(f2[i], sum[t] + sum[Fa[t]] - 2); // case 3
    			Max(f2[i], sum[t]); // case 1
    			Max(f1[i - 1], sum[t]); // case 2
    			if(las != t) Max(f1[i], sum[t]); las = t; // case 2
    		}
    	}
    	R(i, n, 1) Max(f1[i - 1], f1[i]), Max(f2[i - 1], f2[i]);
    	L(i, 1, n) cout << (i % 2 ? f1[i / 2] : f2[i / 2]) << " ";
    	cout << endl;
    	return 0;
    } 
    

    祝大家学习愉快!

  • 相关阅读:
    ZYB建围墙
    换根DP
    原码,反码,补码的计算
    字符串游戏-博弈论-trie
    【十连赛day8】神炎皇
    生成序列
    【USACO 2006 February Silver】产奶安排Stall Reservations-贪心
    tar 压缩解压命令
    java 注解
    回溯算法解决全排列问题
  • 原文地址:https://www.cnblogs.com/zkyJuruo/p/14254120.html
Copyright © 2011-2022 走看看