HYSBZ 2038 小z的袜子(hose) 莫队算法

来源:互联网 发布:免费淘宝海报在线制作 编辑:程序博客网 时间:2024/06/10 23:56

莫队算法真是个神奇的算法。。。


构造曼哈顿距离生成树的搞法主要就是将m个询问[l,r]看成二维平面上的点,如果从区间[l,r]的查询可以O(1)地转移到[l,r+1], 那么从[l,r]转移到[l',r']的花费就是|l-l'|+|r-r'|,也就是曼哈顿距离, 如果构造出m个询问曼哈顿距离最小生成树的话,在树上进行转移,树边的曼哈顿距离之和的级别是nsqrt(n)的(并不知道怎么证明。。。), 然后撸出答案就行了。构造曼哈顿距离生成树是nlog(n)的,转移是nsqrt(n), 总的复杂度是nsqrt(n)。

#include <iostream>#include <cstdio>#include <cstring>#include <algorithm>using namespace std;#define N 50020#define M 200020#define LL long long#define inf 0x3f3f3f3fint a[N], n, m;int x[N], y[N], id[N];LL ans[N];struct edge {    int u, v, c;    bool operator < (const edge &b) const {        return c < b.c;    }}b[M];int tot;int san[N], cnt;int cmp(int i, int j) {    if(x[i] != x[j]) return x[i] < x[j];    return y[i] < y[j];}int mi[N], pos[N], fa[N];int fst[N], nxt[M], vv[M], e;LL X, cc[N];void init() {    memset(fst, -1, sizeof fst);    e = 0;}void add(int u, int v) {    vv[e] = v, nxt[e] = fst[u], fst[u] = e++;}void add_edge(int u, int v) {    ++tot;    b[tot].u = u, b[tot].v = v;    b[tot].c = abs(x[u] - x[v]) + abs(y[u] - y[v]);}void update(int x, int v, int p) {    while(x) {        if(mi[x] > v)            mi[x] = v, pos[x] = p;        x -= x & -x;    }}int query(int x) {    int res = inf, ret = -1;    while(x <= cnt) {        if(res > mi[x])            res = mi[x], ret = pos[x];        x += x & -x;    }    return ret;}int find(int x) {    if(fa[x] != x) fa[x] = find(fa[x]);    return fa[x];}int haxi(int x) {    return lower_bound(san + 1, san + cnt + 1, x) - san;}void ManMst() {    tot = 0;    for(int dir = 0; dir < 4; ++dir) {        if(dir == 1 || dir == 3) {            for(int i = 1; i <= n; ++i)                swap(x[i], y[i]);        }        else if(dir == 2) {            for(int i = 1; i <= m; ++i)                x[i] = -x[i];        }        cnt = 0;        for(int i = 1; i <= m; ++i) {            id[i] = i;            san[++cnt] = y[i] - x[i];            pos[i] = -1;            mi[i] = inf;        }        sort(id + 1, id + m + 1, cmp);        sort(san + 1, san + cnt + 1);        cnt = unique(san + 1, san + cnt + 1) - san - 1;        for(int i = m; i >= 1; --i) {            int u = haxi(y[id[i]] - x[id[i]]);            int v = query(u);            if(v != -1)                add_edge(id[i], v);            update(u, x[id[i]] + y[id[i]], id[i]);        }    }    for(int i = 1; i <= m; ++i) {        fa[i] = i;        y[i] = -y[i];    }    init();    sort(b + 1, b + tot + 1);    for(int i = 1; i <= tot; ++i) {        int u = b[i].u, v = b[i].v;        if(find(u) != find(v)) {            add(u, v);            add(v, u);            fa[find(u)] = find(v);        }    }}void addIt(int l, int r, int v) {    l = max(l, 1);    r = min(r, n);    for(int i = l; i <= r; ++i) {        X -= cc[a[i]] * (cc[a[i]] - 1) / 2;        cc[a[i]] += v;        X += cc[a[i]] * (cc[a[i]] - 1) / 2;    }}void dfs(int l, int r, int ll, int rr, int u, int pre) {    if(ll < l) addIt(ll, l - 1, 1);    if(rr > r) addIt(r + 1, rr, 1);    if(ll > l) addIt(l, ll - 1, -1);    if(rr < r) addIt(rr + 1, r, -1);    ans[u] = X;    for(int i = fst[u]; ~i; i = nxt[i]) {        int v = vv[i];        if(v == pre) continue;        dfs(ll, rr, x[v], y[v], v, u);    }    if(ll < l) addIt(ll, l - 1, -1);    if(rr > r) addIt(r + 1, rr, -1);    if(ll > l) addIt(l, ll - 1, 1);    if(rr < r) addIt(rr + 1, r, 1);}LL gcd(LL a, LL b) {    while(a && b && (a > b? a %= b: b %= a));    return a + b;}int main() {   // freopen("tt.txt", "r", stdin);    while(scanf("%d%d", &n, &m) != EOF) {        for(int i = 1; i <= n; ++i) scanf("%d", &a[i]);        for(int i = 1; i <= m; ++i) scanf("%d%d", &x[i], &y[i]);        ManMst();        X = 0;        memset(cc, 0, sizeof cc);        dfs(0, 0, x[1], y[1], 1, -1);        for(int i = 1; i <= m; ++i) {            LL len = y[i] - x[i] + 1;            len = len * (len - 1) / 2;            if(ans[i] == 0) {                puts("0/1");                continue;            }            LL g = gcd(ans[i], len);            printf("%lld/%lld\n", ans[i] / g, len / g);        }    }    return 0;}

然而当学会曼哈顿距离生成树之后, 不久你就会发现有一个曼哈顿距离生成树精简的替代品(卧槽, 4000b的代码用1500b就搞完了, 卧槽, 有了这个之后绝逼再也不会写曼哈顿距离生成树了), 就是先将n个数分成sqrt(n)块, 然后对询问[l,r],以l所在的块为第一关键字,r为第二关键字排序,然后按照排序后的顺序进行转移就行了。

可以发现, 对于一个块内的询问, r从小到大转移长度最多O(n)次,sqrt(n)个块,所以总的转移是nsqrt(n), l最多有O(n)次移, 每次转移长度不超过sqrt(n),所以总的转移是nsqrt(n), 所以nsqrt(n)就搞出来了。。。

#include <iostream>#include <cstdio>#include <cstring>#include <set>#include <ctime>#include <algorithm>#include <cmath>#include <queue>using namespace std;#define N 50002#define LL long longint B;int pos[N];int n, m, a[N];int x[N], y[N], id[N];LL X, ans[N];LL s[N];LL gcd(LL x, LL y) {while(x && y && (x > y? x %= y: y %= x));return x + y;}bool cmp(int i, int j) {if(pos[x[i]] != pos[x[j]])return pos[x[i]] < pos[x[j]];return y[i] < y[j];}void addIt(int l, int r, int v) {for(int i = l; i <= r; ++i) {X -= s[a[i]] * (s[a[i]] - 1);s[a[i]] += v;X += s[a[i]] * (s[a[i]] - 1);}}int main() {while(scanf("%d%d", &n, &m) != EOF) {B = sqrt(n * 1.0);for(int i = 1; i <= n; ++i) {scanf("%d", &a[i]);pos[i] = i * 1.0 / B;}for(int i = 1; i <= m; ++i) {scanf("%d%d", &x[i], &y[i]);id[i] = i;}sort(id + 1, id + m + 1, cmp);int l = 1, r = 0;memset(s, 0, sizeof s);for(int i = 1; i <= m; ++i) {int u = id[i];if(x[u] < l) addIt(x[u], l - 1, 1);if(y[u] > r) addIt(r + 1, y[u], 1);if(l < x[u]) addIt(l, x[u] - 1, -1);if(r > y[u]) addIt(y[u] + 1, r, -1);l = x[u], r = y[u];ans[u] = X;}for(int i = 1; i <= m; ++i) {if(ans[i] == 0) {puts("0/1");continue;}LL len = y[i] - x[i] + 1;len = len * (len - 1);LL g = gcd(ans[i], len);printf("%lld/%lld\n", ans[i] / g, len / g);}}return 0;}


0 0
原创粉丝点击