1月18日 LCA专项训练

A. Lorenzo Von Matterhorn

B.Minimum spanning tree for each edge

C.Misha, Grisha and Underground

D.Fools and Roads

E.City Driving

题意:给你一颗基环树(有n条边,n个点的连通图),有Q个询问,求u,v之间的距离最小是多少

思路:对于一棵基环树而言,只要去掉其环上任意一条边(a , b),其边权为w ,剩下的边就可以构成一棵树

   对于 u,v 之间的最小距离 , 有可能由去掉的边(a , b)构成 ,也有可能不需要边(a , b)

   不需要L的情况

      ans = dis(u , v)

   需要L的情况

      ans = min(dis(u , a) + dis(v , b) + w , dis(v , a) + dis(u , b) ,w)

   两种情况取min即可。

dfs序+RMQ做法(这个做法虽然有点麻烦,但是查询时O(1)的)

#include<cstdio>
#include<string.h>
#include<algorithm>
#include<cmath>
#include<iostream>
#include<vector>
#include<queue>
#include<set>
#include<map>
#include<stack>
#include<cctype>
#define ios ios::sync_with_stdio(false),cin.tie(0),cout.tie(0)
#define mem(a,x) memset(a,x,sizeof(a))
#define lson rt<<1,l,mid
#define rson rt<<1|1,mid + 1,r
#define P pair<int,int>
#define ull unsigned long long
using namespace std;
typedef long long ll;
const int maxn = 1e5 + 10;
const ll mod = 1e9 + 7;
const int inf = 0x3f3f3f3f;
const long long INF = 0x3f3f3f3f3f3f3f3f;
const double eps = 1e-7;
int n;
int f[maxn];
int qx, qy, qv, cnt, num, si;
struct node
{
    int y, v;
    node(int a, int b)
    {
        y = a;
        v = b;
    }
};
vector<node>G[maxn];
int dp[20][maxn * 2], dis[maxn], vis[maxn], pos[maxn], res[maxn];
// res 存储欧拉序列 pos 存储每个节点第一次出现的位置 

void init()
{
    qx = qy = qv = 0;
    cnt = 0;
    num = 0;
    si = 0;
    mem(dp, 0);
    mem(dis, 0);
    mem(vis, 0);
    mem(res, 0);
    mem(pos, 0);
    for (int i = 0; i < maxn; ++i) G[i].clear();
}

int find(int x)
{
    return x == f[x] ? f[x] : f[x] = find(f[x]);
}
void bset(int x, int y)
{
    int fx = find(x), fy = find(y);
    f[fx] = fy;
}
void dfs(int u, int dist)
{
    vis[u] = 1;
    dis[u] = dist;
    pos[u] = cnt;
    res[si] = u;
    dp[0][cnt++] = si++;
    for (int i = 0; i < G[u].size(); ++i)
    {
        int j = G[u][i].y;
        if (!vis[j])
        {
            dfs(j, dist + G[u][i].v);
            dp[0][cnt++] = dp[0][pos[u]];
        }
    }
}

void RMQ()
{
    for (int i = 1; (1 << i) <= n; ++i)
    {
        for (int j = n - 1; j >= 0; --j)
        {
            int k = (1 << (i - 1));
            dp[i][j] = dp[i - 1][j];
            if (k + j < n)
            {
                dp[i][j] = min(dp[i][j], dp[i - 1][j + k]);
            }
        }
    }
}

int cal(int i, int j)
{
    if (i < j) swap(i, j);
    int k = 0;
    while ((1 << k) <= (i - j + 1))
        ++k;
    --k;
    k = min(dp[k][j], dp[k][i - (1 << k) + 1]);
    return res[k];
}

int  Dis(int u, int v)
{
    int k = cal(pos[u], pos[v]);
    return dis[u] + dis[v] - dis[k] * 2;
}

int main()
{
    while (scanf("%d", &n) != EOF)
    {
        if (n == 0) break;
        init();
        for (int i = 0; i <= n; ++i) f[i] = i;
        for (int i = 1; i <= n; ++i)
        {
            int x, y, v;
            scanf("%d %d %d", &x, &y, &v);
            x++, y++;
            int fx = find(x), fy = find(y);
            if (fx == fy)
            {
                qx = x, qy = y, qv = v;
                continue;
            }
            bset(x, y);
            G[x].push_back(node(y, v));
            G[y].push_back(node(x, v));
        }
        for (int i = 1; i <= n; ++i)
        {
            if (!vis[i])
                dfs(i, 0);
        }
        n = n * 2 - 1;
        RMQ();
        int q;
        scanf("%d", &q);
        while (q--)
        {
            int x, y;
            scanf("%d %d", &x, &y);
            x++, y++;
            int ans = Dis(x, y);
            ans = min(ans, Dis(x, qx) + Dis(y, qy) + qv);
            ans = min(ans, Dis(x, qy) + Dis(y, qx) + qv);
            printf("%d\n", ans);
        }

    }
    return 0;
}

/*

7
0 1 2
0 2 3
1 3 2
2 3 8
2 4 3
3 5 1
1 6 7
3
4 5
0 6
1 2
0

*/

F.Tree chain problem

题意:给你一颗由n个点构成的树,在给你m条链,让你选择一些不相交的链,使得权值最大

思路:用dp[i]记录以i为根节点的子树的权值最大值 , sum[i] 表示点 i 的所有儿子的dp值的和

   考虑到动态规划的无后效性,因此给我们的链我们尽在lca(u , v)处考虑拿或不拿(u , v  为其中一条链的端点)

   这一点的状态dp[i] ,仅有两种状况

      不取 lca(u , v) = i 的这条链

        dp[i] = sum[i]

      取 lca(u , v) = i 的这条链

        dp[i] = ( sum[i] + ∑  (sum[vi] - dp[vi]) + w )  vi 为链上的每一个节点

      因为需要取 lca(u , v) = i 的这条链 因此 vi 点不能取其他的链, 因为题目要求链之间不相交

    为了快速计算  ∑  (sum[vi] - dp[vi]) ,用 dfs序 和  树状数组 来维护 ∑  (sum[vi] - dp[vi])  的前缀和

    当我们求 ∑  (sum[vi] - dp[vi]) 只需要在 i 节点加入 树状数组前 查询 区间[1 , u]  + 区间[1 , v] 的和

#pragma comment(linker,"/STACK:1024000000,1024000000")
#include<algorithm>
#include<iostream>
#include<cstring>
#include<cstdio>
#include<vector>
using namespace std;

const int maxn = 202020;
const int maxb = 22;

struct Node {
    int u, v, w;
    Node(int u, int v, int w) :u(u), v(v), w(w) {}
};

int n, m;
vector<Node> que[maxn];
vector<int> G[maxn];
int lca[maxn][maxb], in[maxn], out[maxn], dep[maxn], dfs_cnt;
int sumv[maxn];// 树状数组
int dp[maxn], sum[maxn];

//计算dfs序,in,out;预处理每个顶点的祖先lca[i][j],表示i上面第2^j个祖先,lca[i][0]表示父亲
void dfs(int u, int fa, int d) {
    in[u] = ++dfs_cnt; // 获取每个点进入的时间
    lca[u][0] = fa; dep[u] = d;
    for (int j = 1; j < maxb; j++) {
        int f = lca[u][j - 1];
        lca[u][j] = lca[f][j - 1];
    }
    for (int i = 0; i < G[u].size(); i++) {
        int v = G[u][i];
        if (v == fa) continue;
        dfs(v, u, d + 1);
    }
    out[u] = ++dfs_cnt; // 获取每个点出去的时间
}

// 倍增法在线求lca ,o(n*logn)预处理+o(logn)询问
int Lca(int u, int v) {
    if (dep[u] < dep[v]) swap(u, v);
    //二进制倍增法,u,v提到相同高度
    for (int i = maxb - 1; i >= 0; i--) {
        if (dep[lca[u][i]] >= dep[v]) u = lca[u][i];
    }
    //当lca为u或者为v的时候
    if (u == v) return u;
    //lca不是u也不是v的情况
    //一起往上提
    for (int i = maxb - 1; i >= 0; i--) {
        if (lca[u][i] != lca[v][i]) {
            u = lca[u][i];
            v = lca[v][i];
        }
    }
    return lca[u][0];
}

//因为需要求出区间[in[u] , out[u]]上的(sum[i] - dp[i])的和 , 用树状数组维护

int get_sum(int x) {
    int ret = 0;
    while (x > 0) {
        ret += sumv[x];
        x -= x & (-x);
    }
    return ret;
}

void add(int x, int v) {
    while (x < maxn) {
        sumv[x] += v;
        x += x & (-x);
    }
}

//树形dp(用到dfs序和树状数组来快速计算链)

void solve(int u, int fa) {
    for (int i = 0; i < G[u].size(); i++) {
        int v = G[u][i];
        if (v == fa) continue;
        solve(v, u);
        sum[u] += dp[v];
    }
    dp[u] = sum[u]; // 先将dp[u]处理为不选择以u为lca的链
    for (int i = 0; i < que[u].size(); i++) {
        Node nd = que[u][i];
        //get_sum(in[nd.u])处理的是lca(u,v)到u点这条路径的所有顶点
        //get_sum(in[nd.v])处理的是lca(u,v)到v点这条路径的所有顶点
        dp[u] = max(dp[u], sum[u] + get_sum(in[nd.u]) + get_sum(in[nd.v]) + nd.w);
    }
    add(in[u], sum[u] - dp[u]);
    add(out[u], dp[u] - sum[u]);
}

void init() {
    dfs_cnt = 0;
    for (int i = 1; i <= n; i++) G[i].clear();
    for (int i = 1; i <= n; i++) que[i].clear();
    memset(lca, 0, sizeof(lca));
    memset(sumv, 0, sizeof(sumv));
    memset(sum, 0, sizeof(sum));
    memset(dp, 0, sizeof(dp));
}

int main() {
    int tc;
    scanf("%d", &tc);
    while (tc--) {
        scanf("%d%d", &n, &m);
        init();
        for (int i = 1; i < n; i++) {
            int u, v;
            scanf("%d%d", &u, &v);
            G[u].push_back(v);
            G[v].push_back(u);
        }
        dfs(1, 0, 1);
        while (m--) {
            int u, v, w;
            scanf("%d%d%d", &u, &v, &w);
            //每条链在Lca的位置上处理,这样符合dp的无后效性
            que[Lca(u, v)].push_back(Node(u, v, w));
        }
        solve(1, 0);
        printf("%d\n", dp[1]);
    }
    for (int i = 1; i <= n; ++i)
    {
        printf("%d ", dp[i]);
    }
    printf("\n");
    for (int i = 1; i <= n; ++i)
    {
        printf("%d ", sum[i]);
    }
    printf("\n");
    return 0;
}

原文地址:https://www.cnblogs.com/DreamACMer/p/12208760.html

时间: 2024-10-09 04:12:28

1月18日 LCA专项训练的相关文章

10月18日随笔

1 #include<iostream> 2 #include<algorithm> 3 #include<cmath> 4 #include<cstring> 5 #include<cstdio> 6 #include<queue> 7 using namespace std; 8 typedef long long LL; 9 inline int read() 10 { 11 int x=0,f=1;char c=getchar

软考信息系统监理师 2016年3月18日作业

软考信息系统监理师,2016年3月18日作业: 第四章 监理单位的组织建设 1.监理单位的体系建设分为哪3部分? 答:分为业务体系建设.质保体系建设.管理体系建设 2.监理单位监理服务质量管理的方式有2种,哪2种?优缺点是什么? 答:一种是以单位管理为主,一种是以监理项目部自我管理为主. 以单位管理为主的质量管理模式的优点是可以保证单位各个监理项目部按照统一 的要求进行监理,易于控制;缺点是限制了总监理工程师质量控制的积极性,管 理费用大.以监理项目部为主的质量管理模式的优点与缺点正好相反. 3

2015年8月18日,杨学明老师《技术部门的绩效管理提升(研讨会)》在中国科学院下属机构CNNIC成功举办!

2015年8月18日,杨学明老师为中国网络新闻办公室直属央企中国互联网络中心(CNNIC)提供了一天的<技术部门的绩效管理提升(研讨会)>培训课程.杨学明老师分别从研发绩效管理概述.研发绩效的考核与评价管理.标杆企业研发绩效管理及结果的应用.激励和奖金分配等方面进行了讲解和研讨.培训过程中,杨学明老师以阿里巴巴.腾讯.华为等企业的互联网产品规划的真实案例为主线,充分发挥学员在学习过程中的智慧,进行了深入的研讨和演练.并给大家分享了来自华为.腾讯.阿里巴巴的一些绩效和激励案例,大家认为受益匪浅.

冲刺阶段(二)第五天 5月18日

昨天:利用前几天学习的知识实现铁大助手中图片切换的功能. 今天:团队测试 困难:不能在所有Android版本模式下运行本软件,有的不能兼容. 冲刺阶段(二)第五天 5月18日,布布扣,bubuko.com

软考信息系统监理师,2016年3月18日作业

软考信息系统监理师,2016年3月18日作业: 第四章 监理单位的组织建设1.监理单位的体系建设分为哪3部分?2.监理单位监理服务质量管理的方式有2种,哪2种?优缺点是什么?3.监理服务质量的控制方式,按照时间可以分为哪3种?按照控制主体分为什么?按照评价方式分为什么?4.监理服务质量控制的内容包括哪些?5.监理单位遵照什么步骤建立和完善质量保证体系?6.监理单位的权利中,应按照""的原则,开展监理工作?7.监理单位的行为准则是什么?8.在行为准则中,什么是公正,什么是独立,什么是科学

谷歌CEO 宣布Google I/O 2016大会将于2016年5月18日Mountain View举办

谷歌CEO Sundar Pichai今日在他的官方Twitter上宣布,Google I/O 2016大会将于2016年5月18日在加州Mountain View拉开帷幕. 这是谷歌一年一度最大的开发者大会.在刚刚过去的2015年,Google I/O大会被评为2015年最受欢迎的开发者大会,位列苹果,微软等各大开发公司大会之首. 你可以通过以下链接订阅Google I/O 2016的新闻已经该大会官方内容. 此外,如果你想体验以下2015年的Google I/O大会,可以观看如下Google

[NOIP集训]10月18日

今天的文件夹:10月18日.zip 今天脑子转不起来,想不出来动规了. Orz @张翰文学神 T1:快排,然后求连续数字的长度,简单判断即可. T2~T4:容我再想两天... T2原题: 题2. 养zsc(pig.pas/c/cpp) [题目描述] 你有一个zsc圈,有N头zsc,每天你最多可以杀一头zsc卖钱,获益就是zsc的体重.但是每过一天每头zsc的体重都会下降P[i](当然,如果zsc体重<=0了,自然获利就是0),问K天内你的最大获利. [输人文件] 第一行两个数N.K: 第二行N个

8月18日全球域名商(国际域名)解析新增保有量TOP20

IDC评述网(idcps.com)08月24日报道:根据DailyChanges公布的最新数据显示,在2015年8月18日,全球域名解析新增保有量二十强大战中,DOMAINCONTROL.COM蝉联冠军,新增保有量6,247个,相比上期有所缩小.另外,本期入围二十强的中国域名商增至4家,分别是中国万网.DNSPod.爱名网.易名中国.下面,请看IDC评述网对相关数据进行详细分析. (图1)全球域名解析商(国际域名)新增保有量TOP20分布图 根据图1,可知在8月18日全球域名解析市场上,DOMA

中国互联:TOP域名11月18日开售

互联网已经成为全球经济命脉 互联网发展30年中,全球人口数目从1985年的48亿增长至2014年71.3亿.据悉,2025年全球人口将突破80亿大关.人口的急速增长的同时将消费市场带向繁荣.在2014年达沃斯论坛中,中华人民共和国国家互联网信息办公室主任指出:"目前中国互联网网站近400万家.中国互联网经济增长速度有两位数,而且是以30%的速度递增." 域名是互联网的敲门砖 在域名发展的历史时间中,可以简单分成:规则--规则+资源两个时段."资源"之声一直是占据着主