您的位置:首页 > 其它

BZOJ 1018|SHOI 2008|堵塞的交通|线段树

2016-04-21 21:07 316 查看
咦,一道自带题解的题目?

题目的坐标表示成(r,c),r=row,行号,c=column,列号。

因此x1,x2只有取1和2两种情况………

考虑线段树维护一格的连通性,合并区间如图:



令总的为r,左半区间a,右半区间b,修改了mid(x,y)。

那么对于r0(上);r1(下);r2(左);r3(右);r4(右下);r5(右上)有:

r_0=a_0&x&b_0|a_4&y&b_5

r_1=a_1&x&b_1|a_5&y&b_3

r_2=a_2|a_0&x&b_2&y&a_1

r_3=a_3|b_0&x&a_3&y&b_1

r_4=a_0&x&b_4|a_4&y&b_1

r_5=a_5&x&b_0|a_1&y&b_5

那么线段树的节点就这么维护这6个值。

x和y就是线段中点的上下边情况。

修改的时候,如果是修改横边,若要修改的就是横跨两子区间的横边,那么就可以直接维护了。如果是修改竖边,若当前区间已经变成一个点,那么直接维护即可。

否则分解区间。

对于查询,注意会有先往两边走再往中间走的情况,因此我们查询要查询左边,当前和右边。查询取出线段后,按照维护的方法类似地判断即可。

orz http://blog.csdn.net/qq_20669971/article/details/50805782 的简洁代码,大家一起%%%

#include <cstdio>
#include <algorithm>
using namespace std;
const int N = 100005;
#define id(i,j) ((i)*(n-1)+(j))
struct Node {
bool w[6];
bool &operator[] (int a) { return w[a]; }
} tree[N * 3], s[2];
int n, mp[N * 2];
void build(int t, int l, int r) {
int mid = l + r >> 1;
if (l == r) tree[t] = s[0];
else build(t * 2, l, mid), build(t * 2 + 1, mid + 1, r);
}
Node join(Node a, Node b, int mid) {
Node r; bool x = mp[id(0, mid)], y = mp[id(1, mid)];
r[0] = a[0] && x && b[0] || a[4] && y && b[5];
r[1] = a[1] && y && b[1] || a[5] && x && b[4];
r[2] = a[2] || a[0] && x && b[2] && y && a[1];
r[3] = b[3] || b[0] && x && a[3] && y && b[1];
r[4] = a[0] && x && b[4] || a[4] && y && b[1];
r[5] = b[0] && x && a[5] || b[5] && y && a[1];
return r;
}
void modify(int t, int l, int r, int x1, int y1, int x2, int y2, bool c) {
int mid = l + r >> 1, y = min(y1, y2);
if (x1 == x2 && y == mid) {
mp[id(x1, y)] = c;
tree[t] = join(tree[t * 2], tree[t * 2 + 1], mid);
} else if (x1 != x2 && l == r) tree[t] = s[c];
else {
if (y <= mid) modify(t * 2, l, mid, x1, y1, x2, y2, c);
if (y > mid) modify(t * 2 + 1, mid + 1, r, x1, y1, x2, y2, c);
tree[t] = join(tree[t * 2], tree[t * 2 + 1], mid);
}
}
Node query(int t, int l, int r, int ql, int qr) {
int mid = l + r >> 1;
if (ql <= l && r <= qr) return tree[t];
if (qr <= mid) return query(t * 2, l, mid, ql, qr);
else if (mid < ql) return query(t * 2 + 1, mid + 1, r, ql, qr);
else return join(query(t * 2, l, mid, ql, mid), query(t * 2 + 1, mid + 1, r, mid + 1, qr), mid);
}
bool ask(int x1, int y1, int x2, int y2) {
if (y1 > y2) swap(x1, x2), swap(y1, y2);
Node a = query(1, 1, n, y1, y2);
Node b = query(1, 1, n, 1, y1);
Node c = query(1, 1, n, y2, n);
if (x1 == x2) return a[x1] || b[3] && a[4 + !x1] || c[2] && a[4 + x1] || b[3] && c[2] && a[!x1];
if (x1 != x2) return a[4 + x1] || b[3] && a[!x1] || c[2] && a[x1] || b[3] && c[2] && a[4 + !x1];
}
int main() {
int x1, y1, x2, y2, i; char op[8];
scanf("%d", &n);
s[0] = (Node) { 1, 1, 0, 0, 0, 0 };
s[1] = (Node) { 1, 1, 1, 1, 1, 1 };
build(1, 1, n);
while (scanf("%s", op), op[0] != 'E') {
scanf("%d%d%d%d", &x1, &y1, &x2, &y2);
--x1; --x2;
if (op[0] == 'O') modify(1, 1, n, x1, y1, x2, y2, 1);
if (op[0] == 'C') modify(1, 1, n, x1, y1, x2, y2, 0);
if (op[0] == 'A') puts(ask(x1, y1, x2, y2) ? "Y" : "N");
}
return 0;
}


1018: [SHOI2008]堵塞的交通traffic

Time Limit: 3 Sec Memory Limit: 162 MB

Submit: 2655 Solved: 877

[Submit][Status][Discuss]

Description

  有一天,由于某种穿越现象作用,你来到了传说中的小人国。小人国的布局非常奇特,整个国家的交通系统可

以被看成是一个2行C列的矩形网格,网格上的每个点代表一个城市,相邻的城市之间有一条道路,所以总共有2C个

城市和3C-2条道路。 小人国的交通状况非常槽糕。有的时候由于交通堵塞,两座城市之间的道路会变得不连通,

直到拥堵解决,道路才会恢复畅通。初来咋到的你决心毛遂自荐到交通部某份差事,部长听说你来自一个科技高度

发达的世界,喜出望外地要求你编写一个查询应答系统,以挽救已经病入膏肓的小人国交通系统。 小人国的交通

部将提供一些交通信息给你,你的任务是根据当前的交通情况回答查询的问题。交通信息可以分为以下几种格式:

Close r1 c1 r2 c2:相邻的两座城市(r1,c1)和(r2,c2)之间的道路被堵塞了;Open r1 c1 r2 c2:相邻的两座城

市(r1,c1)和(r2,c2)之间的道路被疏通了;Ask r1 c1 r2 c2:询问城市(r1,c1)和(r2,c2)是否连通。如果存在一

条路径使得这两条城市连通,则返回Y,否则返回N;

Input

  第一行只有一个整数C,表示网格的列数。接下来若干行,每行为一条交通信息,以单独的一行“Exit”作为

结束。我们假设在一开始所有的道路都是堵塞的。我们保证 C小于等于100000,信息条数小于等于100000。

Output

  对于每个查询,输出一个“Y”或“N”。

Sample Input

2

Open 1 1 1 2

Open 1 2 2 2

Ask 1 1 2 2

Ask 2 1 2 2

Exit

Sample Output

Y

N
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: