NOI2018归程

Problem

传送门

给一张图,每条边有两个参数:\(w,h\),分别代表边长与海拔

有Q个询问,每次询问从给定点出发,借助一辆只能走海拔大于h的边的车后,到一号点最短步行的距离。

Solution

正解是\(kruskal\)重构树,很好理解,也很好打,网上题解很多,这里就不讲了

下面我们讲一下一个需吸氧且随缘\(T\)点的做法

可持久化并查集

有没有感觉十分高端大气上档次?

表示没有

之前上网看过这中算法,但是一直没有看懂

右边大佬这个是怎么实现的

结果

右边大佬:啊?……不就是可持久化那样搞一下……按质合并……就可以了吗?

\(caiji\)表示听不懂……

后来跑到隔壁的时候\(Na_2S_2O_3\ zbr\)提了一嘴

\(Na_2S_2O_3\):你别\(fake\)

\(caiji\):我是真不会

\(zbr\):啊?就是拿个可持久化数组维护一下并查集就可以了啊

\(caiji\):可持久化数组是什么?

\(Na_2S_2O_3\):就是用主席树维护一个数组啊……

(恍然大雾.jpg)

咳咳,跑题了。

现在来讲一讲这道题

先考虑离线,就是先跑一边最短路

询问与边都按从大到小排序,然后每次询问倒序进去的时候按比海拔高的约束加入能开车的边,

这个东西可以用并查集维护。

每次维护这个联通块中到一号点的最小距离,直接输出就好了。

接下来就是在线了

某位伟大的博士曾说过

勇矢博士:所有可离线做的问题都可以用可持久化数据结构在线做。

好像很有道理……

所以我们这道题可以用可持久化并查集……

对于每个询问,我们只需回到没有添加海拔比\(q\)小的路的版本,再查询\(v\)所在的联通块即可

复杂度\(O(n(logn)^2)\)

卡卡常,好像能过个鬼

拷了个海波的快输还是T了两点

Code

#include<bits/stdc++.h>
#define mp make_pair
#define fst first
#define snd second
#define LL long long
#define pli pair<LL,int>
#define pii pair<int,int>

using namespace std;

inline int read(){
    int res = 0, fl = 1;
    char r = getchar();
    for (; !isdigit(r); r = getchar()) if(r == '-') fl = -1;
    for (; isdigit(r); r = getchar()) res = (res << 3) + (res << 1) + r - 48;
    return res * fl;
}

char _obuf[1 << 20], _stk[20];
class Ostream
{
    char *opos, *oend, *stkpos;

public :
    Ostream()
    {
        oend = (opos = _obuf) + (1 << 20);
        stkpos = _stk;
    }

    ~Ostream()
    { fwrite(_obuf, 1, opos - _obuf, stdout); }

    void Putchar(char ch)
    {
        *opos++ = ch;
        if(opos == oend)
        {
            fwrite(_obuf, 1, 1 << 20, stdout);
            opos = _obuf;
        }
    }

    Ostream& operator<<(int n)
    {
        do
        {
            *++stkpos = n % 10 ^ 48;
            n /= 10;
        } while(n);
        while(stkpos != _stk)
            Putchar(*stkpos--);
        return *this;
    }

    Ostream& operator<<(char c)
    {
        Putchar(c);
        return *this;
    }

    Ostream& operator<<(const char* str)
    {
        while(*str != '\0')
            Putchar(*str++);
        return *this;
    }
} out;

inline void chkmin(int &A, int &B) { A = min(A, B);}
inline void chkmax(int &A, int &B) { A = max(A, B);}

const int Maxm = 4e5 + 10, Maxn = 2e5 + 10;

vector <pii> g[Maxn];

struct node{
    int u, v, h;
    bool operator < (const node A) const{ return h > A.h;}
}Map[Maxm];

int n, m, Q, K, S, ans;
int root[Maxn], dis[Maxn];

namespace CMT{
#define mid ((l + r) >> 1)
    int ls[Maxn << 6], rs[Maxn << 6], cnt, root[Maxm << 2], Tim;
    struct TRE{
        int fa, siz, dis;
    }tre[Maxn << 6];
    inline void build(int &rt, int l, int r){
        rt = ++cnt;
        if(l == r) {tre[rt].fa = l, tre[rt].siz = 1, tre[rt].dis = dis[l]; return;}
        build(ls[rt], l, mid);
        build(rs[rt], mid + 1, r);
    }
    inline void Init(){ cnt = 0; build(root[1], 1, n); Tim = 0;}
    inline int Query(int Begin, int rt, int l, int r, int pos){
        if(l == r){return tre[rt].fa == l ? rt : Query(Begin, Begin, 1, n, tre[rt].fa);}
        if(mid >= pos) return Query(Begin, ls[rt], l, mid, pos);
        else return Query(Begin, rs[rt], mid + 1, r, pos);
    }
    inline void Modify_fa(int grt, int &rt, int l, int r, int pos, int fa){
        rt = ++cnt, ls[rt] = ls[grt], rs[rt] = rs[grt], tre[rt] = tre[grt];
        if(l == r) { tre[rt].fa = fa; return;}
        if(mid >= pos) Modify_fa(ls[grt], ls[rt], l, mid, pos, fa);
        else Modify_fa(rs[grt], rs[rt], mid + 1, r, pos, fa);
    }
    inline void Modify(int grt, int &rt, int l, int r, int pos, int siz, int Dis){
        rt = ++cnt, ls[rt] = ls[grt], rs[rt] = rs[grt], tre[rt] = tre[grt];
        if(l == r){
            tre[rt].siz += siz, chkmin(tre[rt].dis, Dis);
            return;
        }
        if(mid >= pos) Modify(ls[grt], ls[rt], l, mid, pos, siz, Dis);
        else Modify(rs[grt], rs[rt], mid + 1, r, pos, siz, Dis);
    }
    inline int link(int u, int v){
        register int rfu = Query(root[Tim << 1 | 1], root[Tim << 1 | 1], 1, n, u);
        register int rfv = Query(root[Tim << 1 | 1], root[Tim << 1 | 1], 1, n, v);
        if(tre[rfu].fa == tre[rfv].fa) return 0;
        Tim++;
        if(tre[rfu].siz > tre[rfv].siz) swap(rfu, rfv);
        Modify_fa(root[(Tim << 1) - 1], root[Tim << 1], 1, n, tre[rfu].fa, tre[rfv].fa);
        Modify(root[Tim << 1], root[Tim << 1 | 1], 1, n, tre[rfv].fa, tre[rfu].siz, tre[rfu].dis);
        return Tim << 1 | 1;
    }
    inline LL Query_dis(int Time, int v){
        return tre[Query(root[Time], root[Time], 1, n, v)].dis;
    }
#undef mid
}

bitset<Maxn> vis, clean;

inline void dijstra(){
    priority_queue <pii> Q;
    memset(dis, 0x3f, sizeof dis);
    vis = clean;
    Q.push(mp(0, 1));
    dis[1] = 0;
    while(!Q.empty()){
        register int now = Q.top().snd;
        Q.pop();
        if(vis[now]) continue;
        vis[now] = 1;
        for (register int i = g[now].size() - 1; i >= 0; --i){
            register int nxt = g[now][i].fst;
            if(dis[nxt] > dis[now] + g[now][i].snd)
                dis[nxt] = dis[now] + g[now][i].snd,
                Q.push(mp(-dis[nxt], nxt));
        }
    }
}
set<pii> Tim;
inline void work(int &T){
    n = read(), m = read(), ans = 0;
    for (register int i = 1; i <= m; ++i){
        register int u = read(), v = read(), w = read(), h = read();
        Map[i] = (node){u, v, h};
        g[u].push_back(mp(v, w));
        g[v].push_back(mp(u, w));
    }
    dijstra();
    sort(Map + 1, Map + 1 + m);
    CMT::Init();
    for (register int i = 1; i <= m; ++i){
        register int u = Map[i].u, v = Map[i].v;
        register int TIME = CMT::link(u, v);
        if(TIME)
            Tim.insert(mp(Map[i].h, -TIME));
    }
    Q = read(), K = read(), S = read();
    Tim.insert(mp(S + 1, -1));
    Tim.insert(mp(0, -(CMT::Tim << 1 | 1)));
    while(Q--){
        register int v = ((LL)read() + K * ans - 1) % n + 1;
        register int p = ((LL)read() + K * ans) % (S + 1);
        pii Time = *Tim.upper_bound(mp(p, 1e9));
        ans = CMT::Query_dis(-Time.snd, v);
        out << ans << '\n';
    }
    if(T) Tim.clear();
    if(T) for (register int i = 1; i <= n; ++i) g[i].clear();
}
int main(){
    freopen("a.in","r",stdin);
    freopen("a.out","w",stdout);
    int T = read();
    while(T--) work(T);
    return 0;
}

转载于:https://www.cnblogs.com/LZYcaiji/p/10622087.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值