[Luogu P4172] [BZOJ 2594] [WC2006]水管局长

26 篇文章 0 订阅
洛谷传送门
BZOJ传送门(卡常, 慎入)

题目描述

SC 省 MY 市有着庞大的地下水管网络,嘟嘟是 MY 市的水管局长(就是管水管的啦),嘟嘟作为水管局长的工作就是:每天供水公司可能要将一定量的水从 x x 处送往 y 处,嘟嘟需要为供水公司找到一条从 A A B 的水管的路径,接着通过信息化的控制中心通知路径上的水管进入准备送水状态,等到路径上每一条水管都准备好了,供水公司就可以开始送水了。嘟嘟一次只能处理一项送水任务,等到当前的送水任务完成了,才能处理下一项。

在处理每项送水任务之前,路径上的水管都要进行一系列的准备操作,如清洗、消毒等等。嘟嘟在控制中心一声令下,这些水管的准备操作同时开始,但由于各条管道的长度、内径不同,进行准备操作需要的时间可能不同。供水公司总是希望嘟嘟能找到这样一条送水路径,路径上的所有管道全都准备就绪所需要的时间尽量短。嘟嘟希望你能帮助他完成这样的一个选择路径的系统,以满足供水公司的要求。另外,由于 MY 市的水管年代久远,一些水管会不时出现故障导致不能使用,你的程序必须考虑到这一点。

不妨将 MY 市的水管网络看作一幅简单无向图(即没有自环或重边):水管是图中的边,水管的连接处为图中的结点。

输入输出格式

输入格式:

输入文件第一行为 3 3 个整数: N , M M , Q 分别表示管道连接处(结点)的数目、目前水管(无向边)的数目,以及你的程序需要处理的任务数目(包括寻找一条满足要求的路径和接受某条水管坏掉的事实)。

¥以下 M M 行,每行 3 个整数 x x , y t t ,描述一条对应的水管。 x y y 表示水管两端结点的编号, t 表示准备送水所需要的时间。我们不妨为结点从 1 1 N 编号,这样所有的 x x y 都在范围 [1,N] [ 1 , N ] 内。

以下 Q Q 行,每行描述一项任务。其中第一个整数为 k

k = 1 k   =   1 则后跟两个整数 A A B ,表示你需要为供水公司寻找一条满足要求的从 A A B 的水管路径;

k = 2 k   =   2 ,则后跟两个整数 x x y ,表示直接连接 x x y 的水管宣布报废(保证合法,即在此之前直接连接 x x y 尚未报废的水管一定存在)。

输出格式:

按顺序对应输入文件中每一项 k = 1 k   =   1 的任务,你需要输出一个数字和一个回车/换行符。该数字表示:你寻找到的水管路径中所有管道全都完成准备工作所需要的时间(当然要求最短)。

输入输出样例

输入样例#1:
4 4 3
1 2 2
2 3 3
3 4 2
1 4 2
1 1 4
2 1 4
1 1 4
输出样例#1:
2
3

说明

【约定】

N1000 N ≤ 1000

M100000 M ≤ 100000

Q100000 Q ≤ 100000

测试数据中宣布报废的水管不超过 5000 5000 条;且任何时候我们考虑的水管网络都是连通的,即从任一结点 A A 必有至少一条水管路径通往任一结点 B


注:此处数据为原题数据,原题数据不强(因为存在此题的数据加强版),故将时限由 3 3 秒改为 1 秒。

解题分析

考虑倒着加边, 我们的任务就成了维护两点之间边权最大值并使之尽量小, 所以用LCT维护连通性的方式即可。(可见这篇博客

找一条边的编号可以用 map m a p ,但卡不进BZOJ的加强版的时限QAQ…

代码如下:

#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <algorithm>
#include <cmath>
#include <cctype>
#include <map>
#define R register
#define IN inline
#define gc getchar()
#define W while
#define MX 2000050
#define ls tree[now].son[0]
#define rs tree[now].son[1]
#define dad tree[now].fat
#define base 100005
template <class T>
IN void in(T &x)
{
    x = 0; R char c = gc;
    W (!isdigit(c)) c = gc;
    W (isdigit(c))
    x = (x << 1) + (x << 3) + c - 48, c = gc;
}
int dot, q, top, cnt, line, acnt;
int sta[MX], ans[MX], bel[MX];
bool vis[MX];
struct INFO {int a, b;};
IN bool operator < (const INFO &x, const INFO &y)
{return x.a == y.a ? x.b < y.b : x.a < y.a;}
std::map <INFO, int> mp;
struct Node
{
    int son[2], mx, pos, fat, val;
    bool rev;
}tree[MX];
struct Event
{
    int typ, from, to, cost;
}eve[MX], pre[MX];
namespace DSU
{
    void reset() {for (R int i = 1; i <= dot; ++i) bel[i] = i;}
    int find(R int now) {return now == bel[now] ? now : bel[now] = find(bel[now]);}
}
namespace LCT
{
    IN bool get(R int now) {return tree[dad].son[1] == now;}
    IN bool nroot(R int now) {return tree[dad].son[1] == now || tree[dad].son[0] == now;}
    IN void pushup(R int now)
    {
        tree[now].mx = tree[now].val, tree[now].pos = now;
        if(tree[ls].mx > tree[now].mx) tree[now].mx = tree[ls].mx, tree[now].pos = tree[ls].pos;
        if(tree[rs].mx > tree[now].mx) tree[now].mx = tree[rs].mx, tree[now].pos = tree[rs].pos;
    }
    IN void pushrev(R int now) {std::swap(ls, rs), tree[now].rev ^= 1;}
    IN void pushdown(R int now) {if(tree[now].rev) pushrev(ls), pushrev(rs), tree[now].rev = false;}
    IN void rotate(R int now)
    {
        R bool dir = get(now);
        R int fa = dad, grand = tree[fa].fat;
        tree[fa].son[dir] = tree[now].son[dir ^ 1];
        tree[tree[now].son[dir ^ 1]].fat = fa;
        if(nroot(fa)) tree[grand].son[get(fa)] = now;
        tree[now].fat = grand;
        tree[fa].fat = now;
        tree[now].son[dir ^ 1] = fa;
        pushup(fa);
    }
    IN void splay(R int now)
    {
        int tmp = now, fa;
        sta[top = 1] = now;
        W (nroot(now)) sta[++top] = now = dad;
        W (top) pushdown(sta[top--]);
        now = tmp;
        W (nroot(now))
        {
            fa = dad;
            if(nroot(fa)) rotate(get(now) == get(fa) ? fa : now);
            rotate(now);
        }
        pushup(now);
    }
    IN void access(R int now)
    {
        for (R int x = 0; now; x = now, now = dad)
        {splay(now); rs = x; pushup(now);}
    }
    IN void makeroot(R int now)
    {access(now), splay(now), pushrev(now);}
    IN void split(R int x, R int y)
    {makeroot(x); access(y); splay(y);}
    IN void link(R int x, R int y)
    {makeroot(x); tree[x].fat = y;}
    IN void cut(R int x, R int y)
    {split(x, y); tree[x].fat = tree[y].son[0] = 0; pushup(y);}
    IN int findroot(R int now)
    {
        access(now); splay(now);
        W (ls) pushdown(now), now = ls;
        return now;
    }
}
int main(void)
{
    int id, tar;
    in(dot), in(line), in(q);
    DSU::reset();
    for (R int i = 1; i <= line; ++i)
    {
        in(pre[i].from), in(pre[i].to), in(pre[i].cost);
        tree[i + base].val = tree[i + base].mx = pre[i].cost;
        tree[i + base].pos = i + base;
        if(pre[i].from > pre[i].to) std::swap(pre[i].from, pre[i].to);
        mp[{pre[i].from, pre[i].to}] = i;
    }
    for (R int i = 1; i <= q; ++i)
    {
        in(eve[i].typ);
        in(eve[i].from), in(eve[i].to);
        if(eve[i].from > eve[i].to) std::swap(eve[i].from, eve[i].to);
        if(eve[i].typ == 2)
        vis[mp[{eve[i].from, eve[i].to}]] = true;
    }
    for (R int i = 1; i <= line; ++i)
    {
        if(!vis[i])
        {
            if(DSU::find(pre[i].from) != DSU::find(pre[i].to))//并查集降低常数
            LCT::link(pre[i].from, i + base), LCT::link(i + base, pre[i].to), bel[bel[pre[i].from]] = bel[pre[i].to];
            else
            {
                LCT::split(pre[i].from, pre[i].to);
                if(tree[pre[i].to].mx > pre[i].cost)
                {
                    tar = tree[pre[i].to].pos - base;
                    LCT::cut(pre[tar].from, tar + base);
                    LCT::cut(tar + base, pre[tar].to);
                    LCT::link(pre[i].from, i + base);
                    LCT::link(i + base, pre[i].to);
                }
            }
        }
    }
    for (R int i = q; i >= 1; --i)
    {
        if(eve[i].typ == 2)
        {
            LCT::split(eve[i].from, eve[i].to);
            id = mp[{eve[i].from, eve[i].to}];
            if(tree[eve[i].to].mx > pre[id].cost)
            {
                tar = tree[eve[i].to].pos - base;
                LCT::cut(pre[tar].from, tar + base);
                LCT::cut(tar + base, pre[tar].to);
                LCT::link(eve[i].from, id + base);
                LCT::link(id + base, eve[i].to);
            }
        }
        else
        {
            LCT::split(eve[i].from, eve[i].to);
            ans[++acnt] = tree[eve[i].to].mx;
        }
    }
    W (acnt) printf("%d\n", ans[acnt--]);
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值