zoukankan      html  css  js  c++  java
  • JZOJ 6898. 【2020.11.27提高组模拟】第二题(DFS序+线段树)

    JZOJ 6898. 【2020.11.27提高组模拟】第二题

    题解

    • 首先分析操作的实质,其实它相当于把某个点连同子树插入到父亲的位置,并将父亲删除,且其余不变。那么 f f f值的暴力求法则可以不断往父亲上跳,当父亲的子树除去自己的子树外还有黑点时 f + 1 f+1 f+1
    • 如果暴力维护这个过程,复杂度是 O ( n 3 ) O(n^3) O(n3),稍微优化一下可以到 O ( n 2 ) O(n^2) O(n2),这都不难想到,但是仍旧无法通过。
    • 依次考虑每个点 i i i新加入后的贡献,贡献分两种:
    • 1、 i i i到根节点路径上的点对 f i f_i fi的贡献;
    • 2、 i i i的出现对其它若干个 f x f_x fx的贡献。
    • 发现可以两棵用线段树来维护,一棵维护出现的点的个数(即每次新加入点则在 d f n i dfn_i dfni的位置 + 1 +1 +1),一棵维护当前点的加入对还未出现的点的贡献。这两棵线段树都是以DFS序为下标的。
    • 贡献一则直接查询第二棵线段树上对应位置的答案。
    • 贡献二要先预处理每个点 i i i父亲的子树中除去 i i i子树外的编号最小值 x x x,则该最小值出现后能对 i i i的子树中所有点产生贡献 + 1 +1 +1的影响。当加入 x x x之后,先在所有它对应的 i i i子树中查询已出现的点的个数加入答案, 然后在 i i i子树中所有点的贡献 + 1 +1 +1

    代码

    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    using namespace std;
    #define N 800010
    #define ll long long
    #define md 1000000007
    int last[N], nxt[N], to[N], len = 0;
    int last1[N], nxt1[N], to1[N], len1 = 0;
    int fa[N], dfn[N], pr[N], si[N];
    int d[N], g[N * 4][2];
    struct {
    	int s[2], mi;
    }f[N * 4];
    int read() {
    	int s = 0;
    	char x = getchar();
    	while(x < '0' || x > '9') x = getchar();
    	while(x >= '0' && x <= '9') s = s * 10 + x - 48, x = getchar();
    	return s;
    }
    void add(int x, int y) {
    	to[++len] = y;
    	nxt[len] = last[x];
    	last[x] = len;
    }
    void add1(int x, int y) {
    	to1[++len1] = y;
    	nxt1[len1] = last1[x];
    	last1[x] = len1;
    }
    void dfs(int k) {
    	dfn[k] = ++dfn[0];
    	si[k] = 1;
    	for(int i = last[k]; i; i = nxt[i]) dfs(to[i]), si[k] += si[to[i]];
    }
    void make(int v, int l, int r) {
    	if(l == r) f[v].mi = pr[l];
    	else {
    		int mid = (l + r) / 2;
    		make(v * 2, l, mid), make(v * 2 + 1, mid + 1, r);
    		f[v].mi = min(f[v * 2].mi, f[v * 2 + 1].mi);
    	}
    }
    void ins(int v, int l, int r, int x, int y, int c) {
    	if(l == x && r == y) {
    		f[v].s[c] += r - l + 1;
    		g[v][c]++;
    	}
    	else {
    		int mid = (l + r) / 2;
    		f[v * 2].s[c] += g[v][c] * (mid - l + 1), f[v * 2 + 1].s[c] += g[v][c] * (r - mid);
    		g[v * 2][c] += g[v][c], g[v * 2 + 1][c] += g[v][c];
    		g[v][c] = 0;
    		if(y <= mid) ins(v * 2, l, mid, x, y, c); 
    		else if(x > mid) ins(v * 2 + 1, mid + 1, r, x, y, c);
    		else ins(v * 2, l, mid, x, mid, c), ins(v * 2 + 1, mid + 1, r, mid + 1, y, c);
    		f[v].s[c] = f[v * 2].s[c] + f[v * 2 + 1].s[c];
    	}
    }
    int fs(int v, int l, int r, int x, int y, int c) {
    	if(l == x && r == y) return f[v].s[c];
    	int mid = (l + r) / 2;
    	f[v * 2].s[c] += g[v][c] * (mid - l + 1), f[v * 2 + 1].s[c] += g[v][c] * (r - mid);
    	g[v * 2][c] += g[v][c], g[v * 2 + 1][c] += g[v][c];
    	g[v][c] = 0;
    	int ans;
    	if(y <= mid) ans = fs(v * 2, l, mid, x, y, c);
    	else if(x > mid) ans = fs(v * 2 + 1, mid + 1, r, x, y, c);
    	else ans = fs(v * 2, l, mid, x, mid, c) + fs(v * 2 + 1, mid + 1, r, mid + 1, y, c);
    	f[v].s[c] = f[v * 2].s[c] + f[v * 2 + 1].s[c];
    	return ans;
    }
    int fi(int v, int l, int r, int x, int y) {
    	if(x > y) return N;
    	if(l == x && r == y) return f[v].mi;
    	int mid = (l + r) / 2;
    	if(y <= mid) return fi(v * 2, l, mid, x, y);
    	if(x > mid) return fi(v * 2 + 1, mid + 1, r, x, y);
    	return min(fi(v * 2, l, mid, x, mid), fi(v * 2 + 1, mid + 1, r, mid + 1, y));
    }
    int main() {
    	int n = read(), i, j, rt;
    	for(i = 1; i <= n; i++) {
    		fa[i] = read();
    		if(fa[i]) add(fa[i], i); else rt = i;
    	}
    	dfs(rt);
    	for(i = 1; i <= n; i++) pr[dfn[i]] = i;
    	make(1, 1, n);
    	for(i = 1; i <= n; i++) if(i != rt) {
    		d[i] = min(fi(1, 1, n, dfn[fa[i]] + 1, dfn[i] - 1), fi(1, 1, n, dfn[i] + si[i], dfn[fa[i]] + si[fa[i]] - 1));
    		d[i] = min(d[i], fa[i]);
    		add1(d[i], i);
    	}
    	ll sum = 0, ans = 1;
    	for(i = 1; i <= n; i++) {
    		sum = (sum + 1 + fs(1, 1, n, dfn[i], dfn[i], 1)) % md;
    		for(j = last1[i]; j; j = nxt1[j]) {
    			int x = to1[j];
    			sum = (sum + fs(1, 1, n, dfn[x], dfn[x] + si[x] - 1, 0)) % md;
    			ins(1, 1, n, dfn[x], dfn[x] + si[x] - 1, 1);
    		}
    		ins(1, 1, n, dfn[i], dfn[i], 0);
    		ans = ans * sum % md;
    	}
    	printf("%d
    ", ans);
    	return 0;
    }
    
  • 相关阅读:
    HDU 5938 Four Operations 【贪心】(2016年中国大学生程序设计竞赛(杭州))
    HDU 5935 Car 【模拟】 (2016年中国大学生程序设计竞赛(杭州))
    HDU 5934 Bomb 【图论缩点】(2016年中国大学生程序设计竞赛(杭州))
    HDU 5933 ArcSoft's Office Rearrangement 【模拟】(2016年中国大学生程序设计竞赛(杭州))
    HDU 5929 Basic Data Structure 【模拟】 (2016CCPC东北地区大学生程序设计竞赛)
    【转】LaTeX 符号命令大全
    HDU 5922 Minimum’s Revenge 【模拟】 (2016CCPC东北地区大学生程序设计竞赛)
    HDU 5927 Auxiliary Set 【DFS+树】(2016CCPC东北地区大学生程序设计竞赛)
    数据结构之稀疏矩阵
    C++中引用(&)的用法和应用实例
  • 原文地址:https://www.cnblogs.com/LZA119/p/14279487.html
Copyright © 2011-2022 走看看