[BZOJ 3995] [SDOI2015] 道路修建 【线段树维护连通性】

题目链接:BZOJ - 3995

题目分析

这道题..是我悲伤的回忆..

线段树维护连通性,与 BZOJ-1018 类似,然而我省选之前并没有做过  1018,即使它在 ProblemSet 的第一页。

更悲伤的是,这道题有 40 分的暴力分,写个 Kruskal 就可以得到,然而我写了个更快的 DP 。

这本来没有什么问题,然而我的 DP 转移少些了一种情况,于是...爆零。没错,省选前20名可能就我没有得到这 40 分?

不想再多说什么了...希望以后不要再这样 SB 了,如果以后还有机会的话。

还是来说这道题吧。

首先 Orz lzr 神犇,我写的是他的做法。

对于一段区间,我们只需要知道它四个角上的四个格点之间的连通性就可以了,一共有 10 种可能的情况,然而这 10 种情况里有一些是同一种类的,于是可以分为 5 种。

然后注意划分区间的时候是选图中的红色格子,而不是选用题目直接描述的黑色格子。即长度为 1 的区间其实包含了 2 * 2 的 4 个格点。

这样的好处是,非常好写!用两个子区间合成一个新区间的答案时,左区间的右端点和右区间的左端点其实是同一列格点,是重合的,无缝对接,所以只要两端区间的联通状态确定了,整个区间的联通状态也就确定了。

不像另一种做法,还要考虑合并时中间的连边情况,非常非常非常复杂。

这样合并最多就 5 * 5 = 25 种,实际上有些合并是不合法的,最后只有 17 种合并。将它们手动写到一个表里就好了。

然后用线段树维护就好了,当修改一条竖边的边权时,相邻的两个格子都要重新计算。

代码

#include <iostream>
#include <cstdlib>
#include <cstring>
#include <cstdio>
#include <cmath>
#include <algorithm>

using namespace std;

inline void Read(int &Num)
{
	char c = getchar();
	bool Neg = false;
	while (c < ‘0‘ || c > ‘9‘)
	{
		if (c == ‘-‘) Neg = true;
		c = getchar();
	}
	Num = c - ‘0‘; c = getchar();
	while (c >= ‘0‘ && c <= ‘9‘)
	{
		Num = Num * 10 + c - ‘0‘;
		c = getchar();
	}
	if (Neg) Num = -Num;
}

inline int gmin(int a, int b) {return a < b ? a : b;}
inline int gmax(int a, int b) {return a > b ? a : b;}

const int MaxN = 60000 + 5, INF = 999999999;
const int Magic[20][5] = {
	{1, 1, 1}, {1, 2, 2}, {1, 3, 3}, {1, 4, 4},
	{1, 5, 5}, {2, 1, 2}, {2, 3, 2}, {2, 5, 4},
	{3, 1, 3}, {3, 3, 3}, {3, 5, 5}, {4, 1, 4},
	{4, 2, 2}, {4, 4, 4}, {5, 1, 5}, {5, 2, 3},
	{5, 4, 5}
};

int n, m;
int A[MaxN][3];

struct ES
{
	int X[6];
} T[MaxN * 4];

ES operator + (ES e1, ES e2)
{
	ES ret;
	for (int i = 1; i <= 5; ++i) ret.X[i] = INF;
	for (int i = 0; i < 17; ++i)
		ret.X[Magic[i][2]] = gmin(ret.X[Magic[i][2]], e1.X[Magic[i][0]] + e2.X[Magic[i][1]]);
	return ret;
}

ES Calc(int s)
{
	ES ret;
	ret.X[1] = A[s][1] + A[s][2];
	ret.X[2] = A[s][1] + A[s][2] + A[s][0] + A[s + 1][0] - gmax(gmax(A[s][1], A[s][2]), gmax(A[s][0], A[s + 1][0]));
	ret.X[3] = A[s + 1][0] + gmin(A[s][1], A[s][2]);
	ret.X[4] = A[s][0] + gmin(A[s][1], A[s][2]);
	ret.X[5] = gmin(A[s][1], A[s][2]);
	return ret;
}

void Build(int x, int s, int t)
{
	if (s == t)
	{
		T[x] = Calc(s);
		return;
	}
	int m = (s + t) >> 1;
	Build(x << 1, s, m);
	Build(x << 1 | 1, m + 1, t);
	T[x] = T[x << 1] + T[x << 1 | 1];
}

void Change(int x, int s, int t, int Pos)
{
	if (s == t)
	{
		T[x] = Calc(s);
		return;
	}
	int m = (s + t) >> 1;
	if (Pos <= m) Change(x << 1, s, m, Pos);
	else Change(x << 1 | 1, m + 1, t, Pos);
	T[x] = T[x << 1] + T[x << 1 | 1];
}

ES Get(int x, int s, int t, int l, int r)
{
	if (l <= s && r >= t) return T[x];
	int m = (s + t) >> 1;
	ES ret;
	if (r <= m) ret = Get(x << 1, s, m, l, r);
	else if (l >= m + 1) ret = Get(x << 1 | 1, m + 1, t, l, r);
	else ret = Get(x << 1, s, m, l, r) + Get(x << 1 | 1, m + 1, t, l, r);
	return ret;
}

int main()
{
	scanf("%d%d", &n, &m);
	for (int i = 1; i <= n - 1; ++i) Read(A[i][1]);
	for (int i = 1; i <= n - 1; ++i) Read(A[i][2]);
	for (int i = 1; i <= n; ++i) Read(A[i][0]);
	Build(1, 1, n - 1);
	char Str[3];
	ES Ans;
	int x, y, xx, yy, Num, l, r;
	for (int i = 1; i <= m; ++i)
	{
		scanf("%s", Str);
		if (Str[0] == ‘C‘)
		{
			Read(x); Read(y); Read(xx); Read(yy); Read(Num);
			if (x == xx)
			{
				A[gmin(y, yy)][x] = Num;
				Change(1, 1, n - 1, gmin(y, yy));
			}
			else
			{
				A[y][0] = Num;
				if (y != 1) Change(1, 1, n - 1, y - 1);
				if (y != n) Change(1, 1, n - 1, y);
			}
		}
		else
		{
			Read(l); Read(r);
			if (l == r) printf("%d\n", A[l][0]);
			else
			{
				Ans = Get(1, 1, n - 1, l, r - 1);
				printf("%d\n", Ans.X[2]);
			}
		}
	}
	return 0;
}

  

时间: 2024-11-02 23:38:15

[BZOJ 3995] [SDOI2015] 道路修建 【线段树维护连通性】的相关文章

BZOJ 3995 Sdoi2015 道路修建 线段树

题目大意:给定一个2*n的网格图,多次改变某条边的权值或询问y坐标在[l,r]中的2*(r-l+1)个点的MST 这真是一道好题= = 我们用线段树维护每个区间内的MST 然后考虑合并 合并两个区间 我们会加入两条边 这样一定会形成一个环 切掉环上最大边 这题没了 然后就是一坨乱七八糟的细节讨论= = 首先最大边一定在图中的彩色部分内 绿色部分可以O(1)求 我们需要维护的是红色和蓝色部分 然后如果切掉的边是横边或者切掉的竖边不是区间唯一的竖边(如图中蓝色竖边) 那么红框和蓝框直接用左右区间的即

BZOJ 3779 重组病毒 LCT+线段树维护DFS序

题目大意:给定一棵树,初始每个点都有一个颜色,支持三种操作: 1.将某个点到根的路径上所有点染上一种新的颜色 2.将某个点到根的路径上所有点染上一种新的颜色,然后把根设为这个点 3.定义一个点的代价为这个点到根路径上颜色的种类数,求某个点子树中所有点代价的平均值 我真是炖了狗了-- 容易发现这玩应就是个LCT,操作1就是Access,操作2就是Move_To_Root,代价就是一个点到根路径上的虚边数量+1 我们用LCT模拟上述操作,用线段树维护DFS序维护信息,一旦LCT中出现了虚实边的切换,

bzoj 1018 线段树维护连通性

本题将一道LCT的题特殊化(支持加边和删边,询问图的连通性),将图变成了2×m的网格图,然后就神奇地可以用线段树来维护. 对于每个区间[l,r],维护其四个角落之间的连通性(仅仅通过[l,r]这段的边构建起的连通性). 查询[l,r]时,先计算出[1,l-1],[l,r],[r+1,c]这三个线段的连通性,然后将[l,r]的四个角变成并查集的4个点,先用[l,r]中的6种关系更新,在看是否可以从左上角的点通过左边区间绕道左下角,以及从右上角通过右边区间绕道右下角,该并的并起来后直接看查询的点是否

bzoj3995[SDOI2015]道路修建

http://www.lydsy.com/JudgeOnline/problem.php?id=3995 线段树维护连通性. 我们发现,对于一个区间[L,R],我们只需要知道(1,L),(2,L),(1,R)和(2,R)这4个点的之间的连通情况即可. 我们在线段树中,假设当前节点的表示的区间的为[L,R],我们需要知道(1,L),(2,L),(1,R)和(2,R)这4个点的之间的连通情况,但是为了方便,我们记了(1,L),(2,L),(1,R+1)和(2,R+1)这4个点的连通情况. 每个节点记

BZOJ 1018--堵塞的交通(线段树)

1018: [SHOI2008]堵塞的交通traffic Time Limit: 3 Sec  Memory Limit: 162 MBSubmit: 3854  Solved: 1265[Submit][Status][Discuss] Description 有一天,由于某种穿越现象作用,你来到了传说中的小人国.小人国的布局非常奇特,整个国家的交通系统可以被看成是一个2行C列的矩形网格,网格上的每个点代表一个城市,相邻的城市之间有一条道路,所以总共有2C个城市和3C-2条道路. 小人国的交通

[BZOJ 1018] [SHOI2008] 堵塞的交通traffic 【线段树维护联通性】

题目链接:BZOJ - 1018 题目分析 这道题就说明了刷题少,比赛就容易跪..SDOI Round1 Day2 T3 就是与这道题类似的..然而我并没有做过这道题.. 这道题是线段树维护联通性的经典模型. 我们线段树的一个节点表示一个区间的联通性,有 6 个 bool 值,表示这个区间的 4 个角上的点之间的联通性. 然后用两个子区间的联通性和两个子区间之间的连边情况合并出整个区间的联通性. 修改某条边时,先在边的数组中修改,然后从这条边所在的点的线段树叶子开始向上 Update . 询问两

BZOJ 2402 陶陶的难题II 二分答案+斜率优化+树链剖分+线段树维护凸包

题目大意:给定一棵树,每个点有两个坐标(x1,y1)和(x2,y2),多次询问某条链上选择两个点i和j(可以相同),求(y1i+y2j)/(x1i+x2j)的最大值 我竟没看出来这是01分数规划...真是老了... 二分答案ans,问题转化成验证(y1i+y2j)/(x1i+x2j)是否>=ans 将式子变形可得(y1i-ans*x1i)+(y2j-ans*x2j)>=0 加号两边独立,分别计算即可 问题转化为求链上y-ans*x最大的点 令P=y-ans*x 则y=ans*x+P 我们发现这

BZOJ 2124: 等差子序列 线段树维护hash

2124: 等差子序列 Description 给一个1到N的排列{Ai},询问是否存在1<=p1=3),使得Ap1,Ap2,Ap3,…ApLen是一个等差序列. Input 输入的第一行包含一个整数T,表示组数.下接T组数据,每组第一行一个整数N,每组第二行为一个1到N的排列,数字两两之间用空格隔开. Output 对于每组数据,如果存在一个等差子序列,则输出一行“Y”,否则输出一行“N”. Sample Input 2 3 1 3 2 3 3 2 1 Sample Output N Y HI

SDOI2015 道路修建

Description 某国有2N个城市,这2N个城市构成了一个2行N列的方格网.现在该国政府有一个旅游发展计划,这个计划需要选定L.R两列(L<=R),修建若干条专用道路,使得这两列之间(包括这两列)的所有2(R-L+1)个城市中每个城市可以只通过专用道路就可以到达这2(R-L+1)个城市中的任何一个城市.这种专用道路只能在同一行相邻两列的城市或者同一列的两个城市之间修建,且修建需要花费一定的费用.由于该国政府决定尽量缩减开支,因此政府决定,选定L.R后,只修建2(R-L+1)-1条专用道路,