• 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 != '')
                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 << '
    ';
    	}
    	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;
    }
    
  • 相关阅读:
    Java 将Clob字段转换成String字符串
    Java BigDecimal详解
    java循环创建对象应该在循环体内还是循环体外
    Java 通过身份证获取生日和性别
    Java将数据进行分组处理
    Java 2进制和16进制的转换
    Map-HashMap 与 IF 判断内存占用对比
    Groovy脚本-通用SQL开关
    Oracle用户、授权、角色管理
    【docker随笔系列】安装sqlserver
  • 原文地址:https://www.cnblogs.com/LZYcaiji/p/10622087.html
Copyright © 2020-2023  润新知