QGRID code

给定一个 n × m(1 <= m <= 3) 的点网格,网格的边上以及点上都有权值。
初始时所有点的权值都为 0 。
维护两种操作:
1. x1 y1 x2 y2 c 把从 (x1, y1) 到 (x2, y2) 的最短路上的所有节点的权值都增加 c 。保证最短路唯一。
2. x y 询问 (x, y) 的权值。

解:

为了方便,所有编号均从 0 开始。

对于操作1,我们不妨设 x1 <= x2 。
我们先考虑 (x1, y1) 和 (x2, y2) 之间的最短路可以由哪些简单的部分构成:
1. 允许经过整个网格,(x1, y1) 经过最短路走到 (x1, p)。
2. 仅允许经过 [x1, x2] × [0, m-1] ,(x1, p) 经过最短路走到 (x2, q)。
3. 允许经过整个网格,(x2, q) 经过最短路走到 (x2, y2)。
其中 0 <= p, q < m 。

允许经过整个网络,(x, y) 走到 (x, y') 的最短路由以下几个部分构成:
1. 仅允许经过 [0, x] × [0, m-1] ,(x, y) 经过最短路走到 (x, p)。
2. 仅允许经过 [x, n-1] × [0, m-1] ,(x, p) 经过最短路走到 (x, y')。
或者
1'. 仅允许经过 [x, n-1] × [0, m-1] ,(x, y) 经过最短路走到 (x, p)。
2'. 仅允许经过 [0, x] × [0, m-1] ,(x, p) 经过最短路走到 (x, y')。
其中 0 <= p < m 。

综合以上,我们可以把 (x1, y1) 和 (x2, y2) 之间的最短路 用至多 5 个如下特殊的最短路连接起来:
仅允许经过 [L, R] × [0, m-1] ,(p) 走到 (q) 的最短路。
其中,对于确定的区间[L, R],我们把 点(L, p) 简记为 (p),把 点(R, q) 简记为 (q+m)。
其中,0 <= p, q < 2*m。

这样的最短路可以考虑用线段树去维护。

考虑区间[L, R]所维护的信息:
仅经过 [L, R] × [0, m-1] 之内的点,(p) 走到 (q) 的最短路长度 dis[p][q]。

假设两个区间分别是 [L, mid] 和 [mid+1, R] ,如何合并两个区间的答案见如下代码:

 void get(int k, int L, int R, int x, int y, node *res)
{
if (L == x && R == y)
{
memcpy(&res[k], &tree[k], sizeof(node));
return;
}
int mid = (L+R)/;
if (y <= mid)
{
get(k<<, L, mid, x, y, res);
memcpy(&res[k], &res[k<<], sizeof(node));
}
else if (x > mid)
{
get(k<<|, mid+, R, x, y, res);
memcpy(&res[k], &res[k<<|], sizeof(node));
}
else
{
get(k<<, L, mid, x, mid, res);
get(k<<|, mid+, R, mid+, y, res);
for (int i = ; i < m; ++ i)
for (int j = i+; j < m; ++ j)
{
res[k].dis[i][j] = res[k<<].dis[i][j];
res[k].dis[i+m][j+m] = res[k<<|].dis[i+m][j+m];
for (int p = ; p < m; ++ p)
for (int q = ; q < m; ++ q) if (p != q)
{
res[k].dis[i][j] = min(res[k].dis[i][j], res[k<<].dis[i][p+m]+res[k<<|].dis[p][q]+res[k<<].dis[j][q+m]+rc[mid][p]+rc[mid][q]);
res[k].dis[i+m][j+m] = min(res[k].dis[i+m][j+m], res[k<<|].dis[p][i+m]+res[k<<].dis[p+m][q+m]+res[k<<|].dis[q][j+m]+rc[mid][p]+rc[mid][q]);
}
res[k].dis[j][i] = res[k].dis[i][j];
res[k].dis[j+m][i+m] = res[k].dis[i+m][j+m];
}
for (int i = ; i < m; ++ i)
for (int j = ; j < m; ++ j)
{
res[k].dis[i][j+m] = INF;
for (int p = ; p < m; ++ p)
res[k].dis[i][j+m] = min(res[k].dis[i][j+m], res[k<<].dis[i][p+m]+res[k<<|].dis[p][j+m]+rc[mid][p]);
if (m == )
{
for (int p = ; p < m; ++ p)
for (int q = ; q < m; ++ q) if (p != q)
res[k].dis[i][j+m] = min(res[k].dis[i][j+m], res[k<<].dis[i][p+m]+res[k<<|].dis[p][q]+res[k<<].dis[q+m][(-p-q)+m]+res[k<<|].dis[(-p-q)][j+m]+rc[mid][]+rc[mid][]+rc[mid][]);
}
res[k].dis[j+m][i] = res[k].dis[i][j+m];
}
}
}

get

当我们得到了 (x1, y1) 和 (x2, y2) 之间的最短路长度之后,剩下的部分就是找到最短路具体是哪一条路。

注意到,任何一段最短路都能被分割成 O(log n) 段单个区间内两端点 (p) 和 (q) 的最短路。

寻找具体最短路的代码:

 void go_path(int k, int L, int R, int x, int y, node *res, int p, int q, ull c)
{
if (L == x && R == y)
{
Tree[depth[k]][q].add(p < m ? place(L, p) : place(R, p-m), c);
return;
}
int mid = (L+R)/;
if (y <= mid)
go_path(k<<, L, mid, x, y, res, p, q, c);
else if (x > mid)
go_path(k<<|, mid+, R, x, y, res, p, q, c);
else
{
if (p < m && q < m)
{
if (res[k].dis[p][q] == res[k<<].dis[p][q])
{
go_path(k<<, L, mid, x, mid, res, p, q, c);
return;
}
for (int pp = ; pp < m; ++ pp)
for (int qq = ; qq < m; ++ qq)
if (res[k].dis[p][q] == res[k<<].dis[p][pp+m]+res[k<<|].dis[pp][qq]+res[k<<].dis[q][qq+m]+rc[mid][pp]+rc[mid][qq])
{
go_path(k<<, L, mid, x, mid, res, p, pp+m, c);
go_path(k<<|, mid+, R, mid+, y, res, pp, qq, c);
go_path(k<<, L, mid, x, mid, res, q, qq+m, c);
return;
}
}
else if (p >= m && q >= m)
{
if (res[k].dis[p][q] == res[k<<|].dis[p][q])
{
go_path(k<<|, mid+, R, mid+, y, res, p, q, c);
return;
}
for (int pp = ; pp < m; ++ pp)
for (int qq = ; qq < m; ++ qq)
if (res[k].dis[p][q] == res[k<<|].dis[pp][p]+res[k<<].dis[pp+m][qq+m]+res[k<<|].dis[qq][q]+rc[mid][pp]+rc[mid][qq])
{
go_path(k<<|, mid+, R, mid+, y, res, pp, p, c);
go_path(k<<, L, mid, x, mid, res, pp+m, qq+m, c);
go_path(k<<|, mid+, R, mid+, y, res, qq, q, c);
return;
}
}
else
{
if (p > q) swap(p, q);
for (int r = ; r < m; ++ r)
if (res[k].dis[p][q] == res[k<<].dis[p][r+m]+res[k<<|].dis[r][q]+rc[mid][r])
{
go_path(k<<, L, mid, x, mid, res, p, r+m, c);
go_path(k<<|, mid+, R, mid+, y, res, r, q, c);
return;
}
if (m == )
{
for (int pp = ; pp < m; ++ pp)
for (int qq = ; qq < m; ++ qq) if (pp != qq)
if (res[k].dis[p][q] == res[k<<].dis[p][pp+m]+res[k<<|].dis[pp][qq]+res[k<<].dis[qq+m][(-pp-qq)+m]+res[k<<|].dis[(-pp-qq)][q]+rc[mid][]+rc[mid][]+rc[mid][])
{
go_path(k<<, L, mid, x, mid, res, p, pp+m, c);
go_path(k<<|, mid+, R, mid+, y, res, pp, qq, c);
go_path(k<<, L, mid, x, mid, res, qq+m, (-pp-qq)+m, c);
go_path(k<<|, mid+, R, mid+, y, res, -pp-qq, q, c);
return;
}
}
}
}
}

go_path

我们对每个区间 [L, R] 可以先预处理出 以 (p) 为源的最短路树(如果不唯一则取任意一个,由于题目保证了最短路唯一,所以那些不唯一的部分一定不会出现在最短路中),其中0 <= p < 2*m。
而对于每一段单个区间的 (p) 和 (q) 的最短路,在这一段路中的每个点的权值都增加 c ,可以看做是区间[L, R]内,【以 (p) 为根的最短路树中,(q)到根之间所有节点增加 c】。

此时我们考虑询问,(x, y)的权值可以表示成若干个(至多O(log n)个)区间的增量之和,即若干个形如 【在某棵树中某节点的权值】 之和。

于是,我们只需要维护两个树上的操作:
1. 根到某个节点之间的一条链整体增加一个数;
2. 询问某个节点的值。

预处理出每个区间的每一棵最短路树的DFS序后,接下来的问题就变成了:维护一个数列,
1'. 区间增加一个数;
2'. 单点询问。

这个问题可以用线段树或者树状数组解决。

综合以上,整个算法的时间复杂度为 $O(n m^2 \log^2 n + q (m^4 \log n + m^2 \log^2 n) )$ 。

Codechef QGRID的更多相关文章

  1. Codechef SEPT17

    Codechef SEPT17 比赛链接:https://www.codechef.com/SEPT17 CHEFSUM code给定数组 a[1..n] ,求最小的下标 i ,使得 prefixsu ...

  2. 【BZOJ-3514】Codechef MARCH14 GERALD07加强版 LinkCutTree + 主席树

    3514: Codechef MARCH14 GERALD07加强版 Time Limit: 60 Sec  Memory Limit: 256 MBSubmit: 1288  Solved: 490 ...

  3. 【BZOJ4260】 Codechef REBXOR 可持久化Trie

    看到异或就去想前缀和(⊙o⊙) 这个就是正反做一遍最大异或和更新答案 最大异或就是很经典的可持久化Trie,从高到低贪心 WA: val&(1<<(base-1))得到的并不直接是 ...

  4. codechef 两题

    前面做了这场比赛,感觉题目不错,放上来. A题目:对于数组A[],求A[U]&A[V]的最大值,因为数据弱,很多人直接排序再俩俩比较就过了. 其实这道题类似百度之星资格赛第三题XOR SUM, ...

  5. codechef January Challenge 2014 Sereja and Graph

    题目链接:http://www.codechef.com/JAN14/problems/SEAGRP [题意] 给n个点,m条边的无向图,判断是否有一种删边方案使得每个点的度恰好为1. [分析] 从结 ...

  6. BZOJ3509: [CodeChef] COUNTARI

    3509: [CodeChef] COUNTARI Time Limit: 40 Sec  Memory Limit: 128 MBSubmit: 339  Solved: 85[Submit][St ...

  7. CodeChef CBAL

    题面: https://www.codechef.com/problems/CBAL 题解: 可以发现,我们关心的仅仅是每个字符出现次数的奇偶性,而且字符集大小仅有 26, 所以我们状态压缩,记 a[ ...

  8. CodeChef FNCS

    题面:https://www.codechef.com/problems/FNCS 题解: 我们考虑对 n 个函数进行分块,设块的大小为S. 每个块内我们维护当前其所有函数值的和,以及数组中每个元素对 ...

  9. codechef Prime Distance On Tree(树分治+FFT)

    题目链接:http://www.codechef.com/problems/PRIMEDST/ 题意:给出一棵树,边长度都是1.每次任意取出两个点(u,v),他们之间的长度为素数的概率为多大? 树分治 ...

随机推荐

  1. NPOI操作Excel 005:写入空Excel(Winform版)

    前文写了一个BS版本号的导出Excel的样例(http://blog.csdn.net/yysyangyangyangshan/article/details/47904119).对于CS版在保存的地 ...

  2. 项目整理--Echarts前端后台的贯通写法

    项目整理–Echarts前端后台的贯通写法 注:下面所有内容建立在FH admin开源框架和eharts插件基础上,建议观看本案例者进行了解. 业务逻辑 绘制两张图表.分别显示城市空间库和其它数据仓库 ...

  3. Android studio 导入githubproject

    Blog From:http://blog.csdn.net/onlysnail/article/details/45115093 从github下载两个开源项目: PagerSlidingTabSt ...

  4. 用Perl发送邮件小例子

    据传,Perl发送邮件有很多方案,但我只会用Mail::Sender这种方式,也就只能简单谈谈这种方式. 在参考众多网页后,程序书写如下: #!/usr/bin/perl -w use Mail::S ...

  5. 电脑技巧 ADSL如何远程盗号

    ADSL如何远程盗号 开头语: 本文中揭露了黑客攻击ADSL用户,窃取用户名密码的常见方法,读者请勿将其用于不法用途,并提醒所有与此漏洞相关的用户尽快采取措施进行防范. ADSL作为一种宽带接入方式已 ...

  6. 计算机网络系列:2M的宽带指的是下载速度么?

    本篇文章对于不懂网络的小白有点用处.避免以后闹笑话.当然.对大神来说.这都是常识了. 我相信非常多人都有过这个问题:我4M的宽带怎么下载速度才300kb/s啊啊啊.这坑爹的宽带. 我没学的时候我也会这 ...

  7. poj 3105 Expectation 按位统计

    题意: 给n,求sum(i^j)/(n^2),0<=i,j<n.n<10^9 分析: 暴力n^2算法肯定超时.这是logn按位统计算法:按位先算出0出现的个数x,则1出现的个数为n- ...

  8. postgres启动过程分析

    今天来学习一下pg启动的过程. pg的启动命令./bin/postgres -D path/to/data. 1.主体监控进程 postmaster进程进入无限循环,等待客户端请求并为之提供请求的服务 ...

  9. openwrt gstreamer实例学习笔记(一.初始化gstreamer)

    GStreamer 是一个非常强大而且通用的流媒体应用程序框架. GStreamer所具备的很多优点来源于其框架的模块化: GStreamer能够无缝的合并新的插件. 但是, 由于追求模块化和高效率, ...

  10. (转载)常用的Mysql数据库操作语句大全

    打开CMD,进入数据库命令:mysql -hlocalhost -uroot -p 退出数据库:exit 用户管理: 1.新建用户: >CREATE USER name IDENTIFIED B ...