【线段树】bzoj3995 [SDOI2015]道路修建

线段树每个结点维护5个域:

整个区间的MST。

将两个左端点连通,两个右端点不连通,整个区间内选择2*(r-l+1)-2条边的最小生成森林,有两个连通块。

将两个右端点连通,两个左端点不连通,整个区间内选择2*(r-l+1)-2条边的最小生成森林,有两个连通块。

两个左端点不连通,两个右端点也不连通,整个区间内选择2*(r-l+1)-2条边的最小生成森林,有两个连通块。(就是上面一条线,下面一条线)

两个左端点不连通,两个右端点也不连通,整个区间内选择2*(r-l+1)-3条边的最小生成森林,有3个连通块。

合并时讨论5*5*2(两条夹缝里的横边是否都选择)种情况。

/*每个结点维护5个域:左右完全连通,左连通右不连通,右连通左不连通,左右都不连通2,左右都不连通3*/
#include<cstdio>
#include<algorithm>
using namespace std;
#define N 60001
#define INF 1000000000
int n,m,heng[2][N],zong[N];
struct Node
{
	int z1y1,z1y0,z0y1,z0y02,z0y03;
	void reset(){z1y1=z1y0=z0y1=z0y02=z0y03=INF;}
}T[N<<2];
inline void pushup(Node &rt,const Node &ls,const Node &rs,const int &r1,const int &l1,const int &l2)
{
	rt.reset();

	int MIN=min(heng[0][r1],heng[1][r1]),SUM=heng[0][r1]+heng[1][r1];

	/*z1y1+z1y1*/rt.z1y1=ls.z1y1+rs.z1y1+MIN;
	if(l2>1)/*z1y1+z1y0*/rt.z1y0=ls.z1y1+rs.z1y0+MIN;
	if(l2>1)/*z1y1+z0y1*/rt.z1y1=min(rt.z1y1,ls.z1y1+rs.z0y1+SUM);
	/*z1y1+z0y02*/rt.z1y1=min(rt.z1y1,ls.z1y1+rs.z0y02+SUM);
				  rt.z1y0=min(rt.z1y0,ls.z1y1+rs.z0y02+MIN);
	if(l2>1)/*z1y1+z0y03*/rt.z1y0=min(rt.z1y0,ls.z1y1+rs.z0y03+SUM);

	if(l1>1)/*z1y0+z1y1*/rt.z1y1=min(rt.z1y1,ls.z1y0+rs.z1y1+SUM);
	if(l1>1&&l2>1)/*z1y0+z1y0*/rt.z1y0=min(rt.z1y0,ls.z1y0+rs.z1y0+SUM);
	/*z1y0+z0y1不合法*/
	if(l1>1)/*z1y0+z0y02*/rt.z1y0=min(rt.z1y0,ls.z1y0+rs.z0y02+SUM);
	/*z1y0+z0y03不合法*/

	if(l1>1)/*z0y1+z1y1*/rt.z0y1=ls.z0y1+rs.z1y1+MIN;
	if(l1>1&&l2>1)/*z0y1+z1y0*/rt.z0y03=ls.z0y1+rs.z1y0+MIN;
	if(l1>1&&l2>1)/*z0y1+z0y1*/rt.z0y1=min(rt.z0y1,ls.z0y1+rs.z0y1+SUM);
	if(l1>1)/*z0y1+z0y02*/rt.z0y1=min(rt.z0y1,ls.z0y1+rs.z0y02+SUM),
				  		  rt.z0y03=min(rt.z0y03,ls.z0y1+rs.z0y02+MIN);
	if(l1>1&&l2>1)/*z0y1+z0y03*/rt.z0y03=min(rt.z0y03,ls.z0y1+rs.z0y03+SUM);

	/*z0y02+z1y1*/rt.z1y1=min(rt.z1y1,ls.z0y02+rs.z1y1+SUM);
				  rt.z0y1=min(rt.z0y1,ls.z0y02+rs.z1y1+MIN);
	if(l2>1)/*z0y02+z1y0*/rt.z1y0=min(rt.z1y0,ls.z0y02+rs.z1y0+SUM),
						  rt.z0y03=min(rt.z0y03,ls.z0y02+rs.z1y0+MIN);
	if(l2>1)/*z0y02+z0y1*/rt.z0y1=min(rt.z0y1,ls.z0y02+rs.z0y1+SUM);
	/*z0y02+z0y02*/rt.z0y02=ls.z0y02+rs.z0y02+SUM;
				   rt.z0y03=min(rt.z0y03,ls.z0y02+rs.z0y02+MIN);
	if(l2>1)/*z0y02+z0y03*/rt.z0y03=min(rt.z0y03,ls.z0y02+rs.z0y03+SUM);

	if(l1>1)/*z0y03+z1y1*/rt.z0y1=min(rt.z0y1,ls.z0y03+rs.z1y1+SUM);
	if(l1>1&&l2>1)/*z0y03+z1y0*/rt.z0y03=min(rt.z0y03,ls.z0y03+rs.z1y0+SUM);
	/*z0y03+z0y1不合法*/
	if(l1>1)/*z0y03+z0y02*/rt.z0y03=min(rt.z0y03,ls.z0y03+rs.z0y02+SUM);
	/*z0y03+z0y03不合法*/
}
void buildtree(int rt,int l,int r)
{
	if(l==r)
	  {
	  	T[rt].z1y1=zong[l];
	  	return;
	  }
	int m=(l+r>>1);
	buildtree(rt<<1,l,m);
	buildtree(rt<<1|1,m+1,r);
	pushup(T[rt],T[rt<<1],T[rt<<1|1],m,m-l+1,r-m);
}
void update(int p,int v,int rt,int l,int r)
{
	if(l==r)
	  {
	  	zong[p]=v;
	  	T[rt].z1y1=v;
	  	return;
	  }
	int m=(l+r>>1);
	if(p<=m) update(p,v,rt<<1,l,m);
	else update(p,v,rt<<1|1,m+1,r);
	pushup(T[rt],T[rt<<1],T[rt<<1|1],m,m-l+1,r-m);
}
void update(int p,int rt,int l,int r)
{
	if(l==r) return;
	int m=(l+r>>1);
	if(p<=m) update(p,rt<<1,l,m);
	else update(p,rt<<1|1,m+1,r);
	pushup(T[rt],T[rt<<1],T[rt<<1|1],m,m-l+1,r-m);
}
Node query(int ql,int qr,int rt,int l,int r)
{
    if(ql<=l && r<=qr) return T[rt];
    int m=(l+r>>1);
    if(ql<=m && m<qr)
      {
        Node res;
        pushup(res,query(ql,qr,rt<<1,l,m),query(ql,qr,rt<<1|1,m+1,r),m,m-l+1,r-m);
        return res;
      }
    else if(ql<=m) return query(ql,qr,rt<<1,l,m);
    else return query(ql,qr,rt<<1|1,m+1,r);
}
int main()
{
//	freopen("bzoj3995.in","r",stdin);
//	freopen("bzoj3995.out","w",stdout);
	for(int i=1;i<=500000;++i);
	scanf("%d%d",&n,&m);
	for(int i=0;i<2;++i)
	  for(int j=1;j<n;++j)
	    scanf("%d",&heng[i][j]);
	for(int i=1;i<=n;++i)
	  scanf("%d",&zong[i]);
	buildtree(1,1,n);
//	printf("%d\n",query(1,3,1,1,n).z1y1);
//	printf("%d\n",query(1,3,1,1,n).z1y0);
//	printf("%d\n",query(1,3,1,1,n).z0y1);
//	printf("%d\n",query(1,3,1,1,n).z0y02);
//	printf("%d\n",query(1,3,1,1,n).z0y03);
	char op[3];
	int x1,y1,x2,y2,val;
	for(;m;--m)
	  {
	  	scanf("%s",op);
	  	if(op[0]==‘C‘)
	  	  {
            scanf("%d%d%d%d%d",&x1,&y1,&x2,&y2,&val);
            if(y1==y2) update(y1,val,1,1,n);
            else
              {
                if(y1>y2) swap(y1,y2);
                heng[x1-1][y1]=val;
                update(y1,1,1,n);
                update(y2,1,1,n);
              }
          }
        else
          {
          	scanf("%d%d",&y1,&y2);
          	printf("%d\n",query(y1,y2,1,1,n).z1y1);
          }
	  }
	return 0;
}
时间: 2025-01-15 13:54:22

【线段树】bzoj3995 [SDOI2015]道路修建的相关文章

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 3995 Sdoi2015 道路修建 线段树

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

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

题目链接:BZOJ - 3995 题目分析 这道题..是我悲伤的回忆.. 线段树维护连通性,与 BZOJ-1018 类似,然而我省选之前并没有做过  1018,即使它在 ProblemSet 的第一页. 更悲伤的是,这道题有 40 分的暴力分,写个 Kruskal 就可以得到,然而我写了个更快的 DP . 这本来没有什么问题,然而我的 DP 转移少些了一种情况,于是...爆零.没错,省选前20名可能就我没有得到这 40 分? 不想再多说什么了...希望以后不要再这样 SB 了,如果以后还有机会的

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条专用道路,

【线段树】[Luogu P4198]楼房修建

显然要维护斜率区间单调递增 并且第一个必选,后一个比前一个选中的斜率大的必选 考虑如何合并两个区间 我们维护一个least值,least这个值必选,且之后选的都必须严格大于least,Push_Up的时候就像在线段树上二分一样做就好了 这样每次Push_Up是$logn$的,线段树单点修改时$logn$的,所以总复杂度是$O(nlog^2n)$的,再维护一个区间最大值可以做到一些不必要但是可以卡常的剪枝... 1 #include<bits/stdc++.h> 2 #define writel

【BZOJ3073】[Pa2011]Journeys 线段树+堆优化Dijkstra

[BZOJ3073][Pa2011]Journeys Description Seter建造了一个很大的星球,他准备建造N个国家和无数双向道路.N个国家很快建造好了,用1..N编号,但是他发现道路实在太多了,他要一条条建简直是不可能的!于是他以如下方式建造道路:(a,b),(c,d)表示,对于任意两个国家x,y,如果a<=x<=b,c<=y<=d,那么在xy之间建造一条道路.Seter保证一条道路不会修建两次,也保证不会有一个国家与自己之间有道路. Seter好不容易建好了所有道路

线段树(I tree)

Codeforces Round #254 (Div. 2)E题这题说的是给了一个一段连续的区间每个区间有一种颜色然后一个彩笔从L画到R每个区间的颜色都发生了 改变然后 在L和R这部分区间里所用的颜色变成了x 然后每个区间的 色度加上abs(x-Yi) Yi 为原位置的颜色,还有一个操作就是求 L 到 R 的距离之内的所有的点和,数据 有 n<=100000 m<100000 次操作 对于每次第二种操作输出, 自然我们将一个区间的颜色如果相同自然将他们 用延迟标记 但是 会有一个问题就是在一个

线段树分治

2014徐寅展论文<线段树在一类分治问题上的应用>读后感. 线段树分治 线段树分治其实就是有撤销操作的时间分治. 题目让你维护一些信息,每次可以询问,可以执行一种操作,也可以将之前的某个这种操作撤回. 操作容易维护,但撤回操作不容易维护. 需要将操作,询问都离线下来.将时间轴画出来,那么每个操作只在时间轴上的一个区间内生效. 用线段树给这个区间打上这个操作的标记,维护信息. TJOI2018 数学计算 小豆现在有一个数x,初始值为1. 小豆有Q次操作,操作有两种类型: m: x = x * m

[CSP-S模拟测试]:Dash Speed(线段树+并查集+LCA)

题目描述 比特山是比特镇的飙车圣地.在比特山上一共有$n$个广场,编号依次为$1$到$n$,这些广场之间通过$n−1$条双向车道直接或间接地连接在一起,形成了一棵树的结构. 因为每条车道的修建时间以及建筑材料都不尽相同,所以可以用两个数字$l_i,r_i$量化地表示一条车道的承受区间,只有当汽车以不小于$l_i$且不大于$r_i$的速度经过这条车道时,才不会对路面造成伤害. $Byteasar$最近新买了一辆跑车,他想在比特山飙一次车.$Byteasar$计划选择两个不同的点$S,T$,然后在它