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
- HYSBZ 2038 小z的袜子(hose) 莫队算法
- HYSBZ 2038 小Z的袜子(hose) (莫队算法)
- 文章标题 HYSBZ 2038 : 小Z的袜子(hose) (莫队算法)
- HYSBZ 2038 小Z的袜子(hose)
- 小Z的袜子(hose) - bzoj 2038 莫队算法
- BZOJ 2038 小Z的袜子(hose) 莫队算法
- BZOJ-2038 小Z的袜子(hose) (莫队算法)
- (莫队算法)2038: 小Z的袜子(hose)
- BZOJ 2038 小Z的袜子(hose) [莫队算法]
- 小Z的袜子(hose) HYSBZ
- 小Z的袜子(hose) HYSBZ
- 小Z的袜子(hose) HYSBZ
- HYSBZ 2038 小Z的袜子 莫队算法
- 小Z的袜子(hose)(莫队算法)
- 【BZOJ2038】小Z的袜子(hose)(莫队算法 + 分块)
- BZOJ 2038([2009国家集训队]小Z的袜子(hose)-莫队算法序列)
- BZOJ 2038 2009国家集训队 小Z的袜子(hose) 莫队算法
- 2038: [2009国家集训队]小Z的袜子(hose) 莫队算法
- Codeforces Div.301D Bad Luck Island(概率dp+记忆化搜索)
- 动态规划最长递增子序列问题
- ZooKeeper编程
- DirectX9 D3DX几何体对象
- 微调 一些改动
- HYSBZ 2038 小z的袜子(hose) 莫队算法
- 用PyOpenGL叩开3D的心扉——OpenGL全解析(3)
- SQL的数据仓库SSAS项目部署
- 6个炒菜习惯 丢失营养
- java并发(2)-优先级,让步,后台线程
- ctype.h里的函数(c语言)
- FZU 2129 子序列个数
- 黑马程序员-----IO流
- 全胃切除后饮食等注意事项