• 常州模拟赛d3t3 两只怪物心心相印


    题目背景

    从前我是一位无名的旅人,旅途中我得到了某样东西:贤者之石。我因此得到悠久的时光和漂泊的生命。1897年冬天,我一时兴起舍弃了旅人的生活。

    贤者之石创造出来的,是货真价实的黄金。我的名声传遍了整个国家。

    题目描述

    炼金术的要素,是炼金树和贤者之石。

    炼金树是一棵含有n个节点的无根树,树上一共有n-1条石榴色的无向边。第i条无向边长度为Li。

    我一共会使用m次炼金术。

    每次使用炼金术时,我首先会把一块石榴色的贤者之石放在炼金树的某个节点x1上。然后,我会将若干块蓝色的贤者之石放在炼金树的其它K1个节点上。保证一个节点不会同时放入两块贤者之石。然后,我会使石榴色的贤者之石熔化。熔化后的贤者之石,只能沿着石榴色的边流动,既不会与蓝色的贤者之石接触,也不会进入蓝色的贤者之石所处的节点内。凝固后,含有石榴色的贤者之石的节点集合记为S1。很显然,S1是一个包含x1的连通块。记S1的大小为|S1|。

    对于上图所示的炼金树,如果我把石榴色的贤者之石放在5号节点,把4块蓝色的贤者之石分别放在1、3、7、9号节点,那么熔化后石榴色的贤者之石就会流到2、5、6、8号节点。此时S1={2,5,6,8},|S1|=4。 然后,取出所有的贤者之石,选择x2、K2和新的K2个节点,用相同的方法,可以再次确定一个节点集合S2。记S2的大小为|S2|。你可以认为,确定S1的过程和确定S2的过程是互不干扰的。 最后,从S1中的每个节点分别向S2中的每个节点连一条长度为W的金色的有向边。注意,在同一次炼金术中连接的金色边长度都相同。

    很显然,每次使用炼金术,都会增加|S1|*|S2|条有向边。 由于贤者之石熔化后只能沿石榴色的边流动,因此每次炼金术是相互独立的。

    有时S1里面只会包含1个节点x,那么我可以不需要使用蓝色的贤者之石,直接将石榴色的贤者之石放在x号节点即可确定S1,此时令K1=-1。当然,我也可以用上述方法确定S2,此时令K2=-1。

    使用完m次炼金术后,我会在1号节点灌入液态的黄金。黄金既可以沿石榴色的边流动,也可以沿金色的有向边流动。很显然,黄金流入i号节点的时间,就是1号节点到i号节点的最短路。

    我想要知道,使用完m次炼金术后,黄金从1号节点流入每个节点的时间。

    输入输出格式

    输入格式:

    第一行包括一个整数Test,表示测试点的编号。

    第二行包括两个整数n,m。

    接下来n-1行,第i行三个整数Ui,Vi,Li,表示一条从Ui到Vi长度为Li的无向边。

    接下来5m行,表示m次炼金术。每次炼金术占五行:

    第一行和第二行确定S1:

    第一行一个整数X1,表示S1中石榴色的贤者之石的初始位置。

    第二行第一个整数K1,

    若K1=-1,表示S1 = {X1}。

    若K1≥0,接下来K1个整数,表示K1块蓝色的贤者之石的位置。

    第三行和第四行确定S2:

    第三行一个整数X2,表示S2中石榴色的贤者之石的初始位置。

    第四行第一个整数K2,

    若K2=-1,表示S2 = {X2}。

    若K2≥0,接下来K2个整数,表示K2块蓝色的贤者之石的位置。

    第五行一个整数W,表示本次炼金术所连的所有金色边长度为W。

    输入数据保证,

    若K1≥0,则第二行K1个整数互不相同且不等于X1。

    若K2≥0,则第四行K2个整数互不相同且不等于X2。

    S1和S2可能有交集。保证0≤Li,W≤10^9。

    输出格式:

    共n行,每行一个整数,第i行表示1号点到i号点的最短路长度。

    输入输出样例

    输入样例#1:
    0
    6 2
    1 2 10
    2 3 10
    2 4 10
    1 5 10
    5 6 1
    1
    -1
    3
    -1
    5
    2
    3 3 4 5
    5
    2 3 6
    3
    输出样例#1:
    0
    3
    5
    3
    3
    4
    
    
    
    
    
    
    【样例1解释】
     
    初始情况下,1号点到每个点的最短路为0,10,20,20,10,11。
    第一次炼金术在原图中添加了一条从1到3,长度为5的有向边。
    第一次炼金术后,1号点到每个点的最短路为0,10,5,20,10,11。
    第二次炼金术:
    X1=2,在3,4,5号节点放入蓝色的贤者之石,所以石榴色的贤者之石能流到1,2号节点。
    注意石榴色的贤者之石不能流到6号节点,因为要流到6号节点必须经过5号节点。
    所以S1 = {1,2}。
    X2=5,在3,6号节点放入蓝色的贤者之石,所以石榴色的贤者之石能流到1,2,5号节点。
    所以S2 = {1,2,4,5}。
    S1中的每个节点分别向S2中的每个节点连长度为3的有向边,一共连了8条有向边。
    第二次炼金术后,1号点到每个点的最短路为0,3,5,3,3,4。

    说明

    n <= 10^5

    m <= 20000

    分析:这道题真的是太变态了,正解的难度和noip2016d1t2差不多.直接暴力spfa得分大概50分左右,最大的困难是要怎么连边,最短路大家都会,两两连边n高达10^5,数组都开不下,这个时候可以加过渡点,方法类似于:传送门,这样可以通过80%左右的数据,超时的原因还是因为边太多,观察条件可以发现连边的点构成的是一个区间,也就是说一组边向另外一组边连边,其实是一个区间向一个区间连边,树上区间,可以联想到dfs序+线段树来处理.

          这个处理比较难,首先要开两个邻接表,一个是为了dfs的初始连边,一个是区间连边(实际上是dfs序),而且我们把区间简化为一个点,这个点不能和1~n中的任何一个点重复.

          首先是建树,为了保证不会出现死循环,我们必须建两棵线段树,为什么?因为我们要有子区间到大区间的路径又要有大区间到子区间的路径,这些路径的边权都是0,如果不建两棵线段树就会死循环。这两棵线段树的连边顺序也必须区分清楚,第一个从小区间连向大区间,第二个从大区间连向小区间,考虑这样一个图:

    中间的是过渡点,事实上我们的路径是:点--->区间--->点--->区间--->点,build操作负责第一四个操作,update负责第二三个操作,脑补一下就清楚了.线段树采用的是动态加点,因为我们根本不知道会有几个数.

         现在考虑最难的dfs序问题,每个点的dfs序包含的不是它的子树吗?这样不就会包含多余的点吗?我们要怎么做才能让它只包含我们所需要的点呢?假设贤者之石放在点x,如果放置石头的点y不是x的祖先,那么我们要选出尽量多的子树不重叠的y出来,重叠的话合并一下成一棵子树.具体的方法是按照右端点排序,然后像单调栈那样处理.如果y是x的祖先怎么办?利用倍增将x向上跳,跳到y下面一个位置,记录最大的L,最小的R,为什么要这样呢?显然如果有很多祖先,肯定是取最近的祖先。因为每一个石头的子树都不能在区间里,所以我们前一个的右端点和后一个的左端点配对就可以了,具体是为什么我也不清楚QAQ,看看代码应该能理解.考场上还是打暴力吧......

    300行代码:

         

    #include <cstdio>
    #include <cstring>
    #include <iostream>
    #include <algorithm>
    #include <cmath>
    #include <queue>
    
    using namespace std;
    
    const int maxn = 5000006;
    const long long inf = 1LL << 60;
    
    int test, n, m, head1[maxn], to1[maxn], nextt1[maxn], w1[maxn], tot1 = 1,x,k,L,R,tott,y,top,ww;
    long long ans[maxn], d[maxn];
    int head[maxn], to[maxn], nextt[maxn], w[maxn], tot = 1,deep[maxn],fa[maxn][17],c[maxn][2],l[maxn],r[maxn],id[maxn],dfs_clock,cnt,root0,root1;
    bool vis[maxn];
    
    struct node
    {
        int l, r;
    }a[maxn],b[maxn];
    
    void add1(int x, int y, int z)
    {
        w1[tot1] = z;
        to1[tot1] = y;
        nextt1[tot1] = head1[x];
        head1[x] = tot1++;
    }
    
    void add(int x, int y, int z)
    {
        w[tot] = z;
        to[tot] = y;
        nextt[tot] = head[x];
        head[x] = tot++;
    }
    
    void dfs(int u, int from, int d)
    {
        l[u] = ++dfs_clock;
        id[dfs_clock] = u;
        deep[u] = d;
        for (int i = head1[u];i;i = nextt1[i])
        {
            int v = to1[i];
            if (v != from)
            {
                dfs(v, u, d + 1);
                fa[v][0] = u;
                add(l[u], l[v], w1[i]);
                add(l[v], l[u], w1[i]);
            }
        }
        r[u] = dfs_clock;
    }
    
    void build0(int l, int r, int &o)
    {
        if (l == r)
        {
            o = l;
            return;
        }
        o = ++cnt;
        int mid = (l + r) >> 1;
        build0(l, mid, c[o][0]);
        build0(mid + 1, r, c[o][1]);
        add(c[o][0], o, 0);
        add(c[o][1], o, 0);
    }
    
    void build1(int l, int r, int &o)
    {
        if (l == r)
        {
            o = l;
            return;
        }
        o = ++cnt;
        int mid = (l + r) >> 1;
        build1(l, mid, c[o][0]);
        build1(mid + 1, r, c[o][1]);
        add(o, c[o][0], 0);
        add(o, c[o][1], 0);
    }
    
    void update0(int l, int r, int o, int x, int y)
    {
        if (x <= l && r <= y)
        {
            add(o, cnt, 0);
            return;
        }
        int mid = (l + r) >> 1;
        if (x <= mid)
            update0(l, mid, c[o][0], x, y);
        if (y > mid)
            update0(mid + 1, r, c[o][1], x, y);
    }
    
    void update1(int l, int r, int o, int x, int y)
    {
        if (x <= l && r <= y)
        {
            add(cnt, o, ww);
            return;
        }
        int mid = (l + r) >> 1;
        if (x <= mid)
            update1(l, mid, c[o][0], x, y);
        if (y > mid)
            update1(mid + 1, r, c[o][1], x, y);
    }
    
    int climb(int x, int y)
    {
        for (int i = 16; i >= 0; i--)
            if (deep[fa[x][i]] > deep[y])
                x = fa[x][i];
        return x;
    }
    
    bool cmp(node a, node b)
    {
        return a.r < b.r;
    }
    
    void spfa()
    {
        queue <int> q;
        for (int i = 1; i <= cnt; i++)
            d[i] = inf;
        d[1] = 0;
        vis[1] = 1;
        q.push(1);
        while (!q.empty())
        {
            int u = q.front();
            q.pop();
            vis[u] = 0;
            for (int i = head[u]; i;i = nextt[i])
            {
                int v = to[i];
                if (d[v] > d[u] + w[i])
                {
                    d[v] = d[u] + w[i];
                    if (!vis[v])
                    {
                        vis[v] = 1;
                        q.push(v);
                    }
                }
            }
        }
    }
    
    int main()
    {
        scanf("%d%d%d", &test, &n, &m);
        for (int i = 1; i < n; i++)
        {
            int u, v, l;
            scanf("%d%d%d", &u, &v, &l);
            add1(u, v, l);
            add1(v, u, l);
        }
        dfs(1, 0, 1);
        for (int j = 1; j <= 16; j++)
            for (int i = 1; i <= n; i++)
                fa[i][j] = fa[fa[i][j - 1]][j - 1];
        cnt = n;
        build0(1, n, root0);
        build1(1, n, root1);
        while (m--)
        {
            cnt++;
            L = 1;
            R = n;
            scanf("%d%d", &x, &k);
            if (k < 0)
                update0(1, n, root0, l[x], l[x]);
            else
            {
                tott = 0;
                for (int i = 1; i <= k; i++)
                {
                    scanf("%d", &y);
                    if (l[y] <= l[x] && r[x] <= r[y])
                    {
                        int t = climb(x, y);
                        L = max(L, l[t]);
                        R = min(R, r[t]);
                    }
                    else
                    {
                        b[++tott].l = l[y];
                        b[tott].r = r[y];
                    }
                }
                sort(b + 1, b + tott + 1, cmp);
                top = 0;
                for (int i = 1; i <= tott; i++)
                {
                    while (top && a[top].l >= b[i].l)
                        top--;
                    if (!top || a[top].r + 1 < b[i].l)
                        a[++top] = b[i];
                    else
                        a[top].r = b[i].r;
                }
                a[0].r = L - 1;
                a[top + 1].l = R + 1;
                for (int i = 0; i <= top; i++)
                    if (a[i].r + 1 < a[i + 1].l)
                        update0(1, n, root0, a[i].r + 1, a[i + 1].l - 1);
            }
    
            scanf("%d%d", &x, &k);
            L = 1;
            R = n;
            if (k < 0)
            {
                scanf("%d", &ww);
                update1(1, n, root1, l[x], l[x]);
            }
            else
            {
                tott = 0;
                for (int i = 1; i <= k; i++)
                {
                    scanf("%d", &y);
                    if (l[y] <= l[x] && r[x] <= r[y])
                    {
                        int t = climb(x, y);
                        L = max(L, l[t]);
                        R = min(R, r[t]);
                    }
                    else
                    {
                        b[++tott].l = l[y];
                        b[tott].r = r[y];
                    }
                }
                sort(b + 1, b + tott + 1, cmp);
                top = 0;
                for (int i = 1; i <= tott; i++)
                {
                    while (top && a[top].l >= b[i].l)
                        top--;
                    if (!top || a[top].r + 1 < b[i].l)
                        a[++top] = b[i];
                    else
                        a[top].r = b[i].r;
                }
                a[0].r = L - 1;
                a[top + 1].l = R + 1;
                scanf("%d", &ww);
                for (int i = 0; i <= top; i++)
                    if (a[i].r + 1 < a[i + 1].l)
                        update1(1, n, root1, a[i].r + 1, a[i + 1].l - 1);
            }
        }
        spfa();
        for (int i = 1; i <= n; i++)
            ans[id[i]] = d[i];
        for (int i = 1; i <= n; i++)
            printf("%lld
    ", ans[i]);
    
        return 0;
    }
  • 相关阅读:
    Easyui datagrid 修改分页组件的分页提示信息为中文
    Easyui datagrid 实现表格记录拖拽
    Java:内部类
    算法导论:Trie字典树
    算法导论:找零钱问题
    lintcode:组成最大的数
    lintcode:验证二叉查找树
    lintcode:将二叉查找树转换成双链表
    lintcode:二叉树的路径和
    lintcode:字符串置换
  • 原文地址:https://www.cnblogs.com/zbtrs/p/7422703.html
Copyright © 2020-2023  润新知