Luogu 4768 [NOI2018]归程

并不会写Kruskal重构树,两个$log$跑得比较卡。

首先考虑一下没有强制在线的要求怎么办,有一个比较容易想到的做法就是先跑一遍最短路,然后把所有边按照海拔从大到小排序,把所有询问的海拔也从大到小排序,然后对于每一个询问$(x, h)$把所有海拔高于$h$的边都连上,然后看一看点$x$的能到达的点中离$1$距离最小的点是多少。

这就是一个并查集可以维护的东西了。

强制在线怎么办……直接大力把这个并查集可持久化……就没了。

时间复杂度$O(nlog^2n)$。

Code:

#include <cstdio>
#include <cstring>
#include <queue>
#include <iostream>
#include <algorithm>
using namespace std;
typedef pair <int, int> pin;

const int N = 2e5 + 5;
const int M = 5e5 + 5;

int testCase, n, m, qn, tot, head[N], dis[N];
int numCnt, in[M], ed[M], pos[M];
bool vis[N];

struct Edge {
    int to, nxt, val;
} e[M << 1];

inline void add(int from, int to, int val) {
    e[++tot].to = to;
    e[tot].val = val;
    e[tot].nxt = head[from];
    head[from] = tot;
}

struct Pathway {
    int x, y, val, h, id;
} path[M];

bool cmp(const Pathway &u, const Pathway &v) {
    return u.h < v.h;
}

inline void read(int &X) {
    X = 0; char ch = 0; int op = 1;
    for(; ch > '9' || ch < '0'; ch = getchar())
        if(ch == '-') op = -1;
    for(; ch >= '0' && ch <= '9'; ch = getchar())
        X = (X << 3) + (X << 1) + ch - 48;
    X *= op;
}

priority_queue <pin> Q;
void dij() {
    memset(dis, 0x3f, sizeof(dis));
    memset(vis, 0, sizeof(vis));
    Q.push(pin(dis[1] = 0, 1));
    for(; !Q.empty(); ) {
        int x = Q.top().second; Q.pop();
        if(vis[x]) continue;
        vis[x] = 1;
        for(int i = head[x]; i; i = e[i].nxt) {
            int y = e[i].to;
            if(dis[y] > dis[x] + e[i].val) {
                dis[y] = dis[x] + e[i].val;
                Q.push(pin(-dis[y], y));
            }
        }
    }
}

inline int min(int x, int y) {
    return x > y ? y : x;
}

inline void swap(int &x, int &y) {
    int t = x; x = y; y = t;
}

namespace PUfs {
    struct Node {
        int lc, rc, ufs, dis, dep;
    } s[N * 60];
    int root[M], nodeCnt = 0LL;
    
    #define lc(p) s[p].lc
    #define rc(p) s[p].rc
    #define ufs(p) s[p].ufs
    #define dis(p) s[p].dis
    #define dep(p) s[p].dep
    #define mid ((l + r) >> 1)
    
    inline void clear(int p) {
        lc(p) = rc(p) = ufs(p) = dis(p) = dep(p) = 0;
    }
    
    void build(int &p, int l, int r, int type) {
        p = ++nodeCnt;
        if(l == r) {
            ufs(p) = l, dep(p) = 0; 
            if(!type) dis(p) = 0;
            else dis(p) = dis[l];
            return;
        }
        
        build(lc(p), l, mid, type);
        build(rc(p), mid + 1, r, type);
    }
    
    void modify(int &p, int l, int r, int x, int nowUfs, int nowDis, int pre) {
        p = ++nodeCnt;
        if(l == r) {
            ufs(p) = nowUfs, dis(p) = nowDis, dep(p) = dep(pre);
            return;
        }
        
        lc(p) = lc(pre), rc(p) = rc(pre);
        if(x <= mid) modify(lc(p), l, mid, x, nowUfs, nowDis, lc(pre));
        else modify(rc(p), mid + 1, r, x, nowUfs, nowDis, rc(pre));
    }
    
    void mDis(int &p, int l, int r, int x, int nowDis, int pre) {
        p = ++nodeCnt;
        if(l == r) {
            ufs(p) = ufs(pre), dep(p) = dep(pre), dis(p) = nowDis;
            return;
        }
        
        lc(p) = lc(pre), rc(p) = rc(pre);
        if(x <= mid) mDis(lc(p), l, mid, x, nowDis, lc(pre));
        else mDis(rc(p), mid + 1, r, x, nowDis, rc(pre));
    }
    
    void add(int p, int l, int r, int x) {
        if(l == r) {
            ++dep(p);
            return;
        }
        
        if(x <= mid) add(lc(p), l, mid, x);
        else add(rc(p), mid + 1, r, x);
    }
    
    int query(int p, int l, int r, int x) {
        if(l == x && x == r) return p;
        
        if(x <= mid) return query(lc(p), l, mid, x);
        else return query(rc(p), mid + 1, r, x);
    }
        
    int find(int now, int x) {
        int f = query(now, 1, n, x);
        if(s[f].ufs == x) return f;
        else return find(now, s[f].ufs);
    }
    
    inline void merge(int k, int x, int y) {
        root[k] = root[k + 1];
        int fx = find(root[k], x), fy = find(root[k], y);
        if(s[fx].ufs == s[fy].ufs) return;
        if(s[fx].dep > s[fy].dep) swap(fx, fy);
        int dis = min(s[fx].dis, s[fy].dis);
        modify(root[k], 1, n, s[fx].ufs, s[fy].ufs, dis, root[k + 1]);
        if(dis != s[fy].dis) mDis(root[k], 1, n, s[fy].ufs, dis, root[k]);
        if(s[fx].dep == s[fy].dep) add(root[k], 1, n, s[fy].ufs);
    }
    
    #undef mid
    
} using namespace PUfs;

inline void prework() {
    for(int i = 1; i <= nodeCnt; i++) clear(i);
    nodeCnt = 0; memset(root, 0, sizeof(root));
    build(root[m + 1], 1, n, 1);
    build(root[0], 1, n, 0);
}

inline int bfind(int val) {
    int ln = 0, rn = numCnt, mid, res;
    for(; ln <= rn; ) {
        mid = (ln + rn) / 2;
        if(in[mid] <= val) ln = mid + 1, res = mid;
        else rn = mid - 1;
    }
    return res;
}

int main() {
//    freopen("return.in", "r", stdin);
//    freopen("return.out", "w", stdout);
    
    for(read(testCase); testCase--; ) {
        tot = numCnt = 0; memset(head, 0, sizeof(head));
        
        read(n), read(m);
        for(int i = 1; i <= m; i++) {
            read(path[i].x), read(path[i].y), read(path[i].val), read(path[i].h);
            in[++numCnt] = path[i].h;
            path[i].id = i;
            add(path[i].x, path[i].y, path[i].val), add(path[i].y, path[i].x, path[i].val);
        }
        
        sort(in + 1, in + 1 + numCnt);
        numCnt = unique(in + 1, in + 1 + numCnt) - in - 1;
        
/*        for(int i = 1; i <= numCnt; i++)
            printf("%d ", in[i]);
        printf("\n");   */
        
        for(int i = 1; i <= m; i++) 
            path[i].h = lower_bound(in + 1, in + 1 + numCnt, path[i].h) - in;
        
        dij();
        
/*        for(int i = 1; i <= n; i++)
            printf("%d ", dis[i]);
        printf("\n");   */
        
        prework();
        sort(path + 1, path + 1 + m, cmp);
        
/*        printf("\n");
        for(int i = 1; i <= m; i++)
            printf("%d %d %d %d\n", path[i].x, path[i].y, path[i].val, path[i].h);   */
        
        for(int i = m; i >= 1; i--) {
            merge(i, path[i].x, path[i].y);
            if(path[i].h != path[i - 1].h) ed[path[i].h] = i;    
        }    
        
/*        for(int i = 1; i <= numCnt; i++)
            printf("%d ", ed[i]);
        printf("\n");   */
        
        for(int i = 1; i < numCnt; i++) pos[i] = ed[i + 1];
        pos[numCnt] = m + 1;
        
/*        for(int i = 1; i <= numCnt; i++)
            printf("%d ", pos[i]);
        printf("\n");   */
        
/*        for(int j = 1; j <= n; j++)
                printf("%d ", s[query(root[m + 1], 1, n, j)].dis);
            printf("\n");   */
        
/*        for(int i = 1; i <= numCnt; i++) {
            printf("%d: ", i);
            for(int j = 1; j <= n; j++)
                printf("%d ", s[query(root[pos[i]], 1, n, j)].ufs);
            printf("\n");
        }
        
        for(int i = 1; i <= numCnt; i++) {
            printf("%d: ", i);
            for(int j = 1; j <= n; j++)
                printf("%d ", s[query(root[pos[i]], 1, n, j)].dis);
            printf("\n");
        }    */
        
/*        for(int i = 1; i <= numCnt; i++)
            printf("%d ", pos[i]);
        printf("\n");    */
        
//        in[++numCnt] = path[m].h + 1;
        
        int K, S, ans = 0;
        read(qn), read(K), read(S);
        for(int x, h; qn--; ) {
            read(x), read(h);
            x = (x + K * ans - 1) % n + 1, h = (h + K * ans) % (S + 1);
            int rt = bfind(h);
            rt = pos[rt];
            rt = find(root[rt], x);
            printf("%d\n", ans = s[rt].dis);
        }
    }
    return 0;
}
View Code

缅怀$spfa$。

posted @ 2018-09-18 14:30  CzxingcHen  阅读(124)  评论(0编辑  收藏  举报