• 【LG2495】[SDOI2011]消耗战


    【LG2495】[SDOI2011]消耗战

    题面

    洛谷

    题解

    参考博客

    题意

    给你(n)个点的一棵树

    (m)个询问,每个询问给出(k)个点

    求将这(k)个点与(1)号点断掉的最小代价

    其中(nleq250000) (mgeq1) (Sigma k_ileq500000)

    暴力

    考虑直接暴力(dp)

    (dp[i])表示处理完(i)的子树的最小代价

    (dp[i])(=)(minSigma dp[son_i],mn[i])其中(mn[i])代表根节点到(i)节点路径上的最小边权

    这样的话复杂度(O(nm))无法通过此题

    但观察数据范围

    发现利用(Sigma k_i)比较小的特点切入

    虚树

    思想

    就是只将树上有用的点提取出来进行(dp),重构一棵树

    这里指询问点和它们的(lca)

    构建

    考虑如何建一颗虚树。

    首先我们要先对整棵树dfs一遍,求出他们的dfs序,然后对每个节点以dfs序为关键字从小到大排序

    同时维护一个栈,表示从根到栈顶元素这条链。

    设当前要加入的节点为(p),栈顶元素为(x=s[top],lca)为它们的最近公共祖先

    因为我们按照(dfs)序遍历,所以(p)不可能是(lca)

    那么现在会有两种情况

    1.(lca)(x),直接将(p)入栈

    2.(x)(p)位于不同的子树中,则此时(x)所在子树已经遍历完了,我们需对其进行构造

    设栈顶元素为(x),第二个为(y)

    (dfn[y]>dfn[lca]),可连边(y->x),将(x)出栈。

    (dfn[y]=dfn[lca])(y)=(lca),连边(lca->x),子树构建完毕。

    (dfn[y]<dfn[lca]),即(lca)(x)(y)之间,连边(lca->x)(x)出栈,再将(lca)入栈,子树构建完毕。

    然后重复这个过程就可以了,不理解的话可以自己手玩一下。。。

    复杂度

    大约是(O(Sigma k_i的)),可能会带一些小常数

    然后这题的代码贴在这里了:

    #include <iostream>
    #include <cstdio>
    #include <cstdlib>
    #include <cstring>
    #include <cmath>
    #include <algorithm>
    #include <climits>
    #include <vector> 
    using namespace std;
    
    inline int gi() {
        register int data = 0, w = 1;
        register char ch = 0;
        while (ch != '-' && (ch > '9' || ch < '0')) ch = getchar();
        if (ch == '-') w = -1 , ch = getchar();
        while (ch >= '0' && ch <= '9') data = data * 10 + (ch ^ 48), ch = getchar();
        return w * data;
    }
    typedef long long ll;
    #define int ll 
    const int MAX_N = 250005;
    const int MAX_LOG_N = 19; 
    struct Graph { int to, cost, next; } e[MAX_N << 1]; int fir[MAX_N], e_cnt;
    void clearGraph() { memset(fir, -1, sizeof(fir)); e_cnt = 0; } 
    void Add_Edge(int u, int v, int w) { e[e_cnt] = (Graph){v, w, fir[u]}; fir[u] = e_cnt++; } 
    int N, M, s[MAX_N], _top, dfn[MAX_N], mn[MAX_N]; 
    namespace cpp1 {
    	int dep[MAX_N], top[MAX_N], fa[MAX_N], size[MAX_N], son[MAX_N], tim; 
    	void dfs1(int x) {
    		dep[x] = dep[fa[x]] + 1; size[x] = 1; 
    		for (int i = fir[x]; ~i; i = e[i].next) {
    			int v = e[i].to; if (v == fa[x]) continue; 
    			fa[v] = x; mn[v] = min(mn[x], e[i].cost); 
    			dfs1(v);
    			size[x] += size[v];
    			if (size[v] > size[son[x]]) son[x] = v; 
    		} 
    	}
    	void dfs2(int x, int tp) {
    		top[x] = tp; dfn[x] = ++tim;
    		if (son[x]) dfs2(son[x], tp); 
    		for (int i = fir[x]; ~i; i = e[i].next) {
    			int v = e[i].to;
    			if (v == fa[x] || v == son[x]) continue;
    			dfs2(v, v); 
    		} 
    	}
    	int LCA(int x, int y) {
    		while (top[x] != top[y]) { 
    			if (dep[top[x]] < dep[top[y]]) swap(x, y);
    			x = fa[top[x]]; 
    		}
    		return dep[x] > dep[y] ? y : x; 
    	} 
    }
    namespace cpp2 {
    	vector<int> G[MAX_N];
    	void add(int x, int y) { G[x].push_back(y); }
    	void ins(int x) { 
    		if (_top == 1) { s[++_top] = x; return ; }
    		int lca = cpp1::LCA(x, s[_top]);
    		if (lca == s[_top]) return ;
            while (_top > 1 && dfn[s[_top - 1]] >= dfn[lca]) add(s[_top - 1], s[_top]), _top--; 
    		if (lca != s[_top]) add(lca, s[_top]), s[_top] = lca;
    		s[++_top] = x; 
    	} 
    	ll dfs(int x) {
    		if (G[x].size() == 0) return mn[x]; 
    		ll res = 0;
    		for (int i = 0; i < (int)G[x].size(); i++) res += dfs(G[x][i]); 
    		G[x].clear();
    		return min(res, 1ll * mn[x]); 
    	}
    	bool cmp(int a, int b) { return dfn[a] < dfn[b]; } 
    }
    int a[MAX_N];
    #undef int 
    int main () {
    	#define int ll 
    	clearGraph(); 
    	N = gi(); 
    	for (int i = 1; i < N; i++) {
    		int u = gi(), v = gi(), w = gi(); 
    		Add_Edge(u, v, w); 
    		Add_Edge(v, u, w); 
    	}
    	fill(&mn[1], &mn[N + 1], LLONG_MAX / 2); 
    	cpp1::dfs1(1); cpp1::dfs2(1, 1);
    	M = gi(); 
    	while (M--) {
    		int K = gi(); for (int i = 1; i <= K; i++) a[i] = gi(); 
    		sort(&a[1], &a[K + 1], cpp2::cmp);
    		s[_top = 1] = 1;
    		for (int i = 1; i <= K; i++) cpp2::ins(a[i]); 
    		while (_top > 0) cpp2::add(s[_top - 1], s[_top]), _top--;
    		printf("%lld
    ", cpp2::dfs(1)); 
    	} 
        return 0; 
    } 
    
  • 相关阅读:
    接口测试用例设计方法
    接口测试的总结文档
    数据库操作语句类型(DQL、DML、DDL、DCL)简介
    MySQL基础学习笔记
    Python2爬取内涵段子
    Python编程笔记
    Python核心编程笔记--动态属性
    Python核心编程笔记--私有化
    Python核心编程笔记--浅拷贝与深拷贝
    python核心编程笔记--模块的导入
  • 原文地址:https://www.cnblogs.com/heyujun/p/10140146.html
Copyright © 2020-2023  润新知