HDU 5861 离线查询线段树

Road

Time Limit: 12000/6000 MS (Java/Others)    Memory Limit: 65536/65536 K (Java/Others)
Total Submission(s): 1283    Accepted Submission(s): 361


Problem Description
There are n villages along a high way, and divided the high way into n-1 segments. Each segment would charge a certain amount of money for being open for one day, and you can open or close an arbitrary segment in an arbitrary day, but you can open or close the segment for just one time, because the workers would be angry if you told them to work multiple period.

We know the transport plan in the next m days, each day there is one cargo need to transport from village  ai  to village  bi , and you need to guarantee that the segments between  ai  and  bi  are open in the i-th day. Your boss wants to minimize the total cost of the next m days, and you need to tell him the charge for each day.

(At the beginning, all the segments are closed.)
 

Input
Multiple test case. For each test case, begins with two integers n, m(1<=n,m<=200000), next line contains n-1 integers. The i-th integer  wi (1<= wi <=1000) indicates the charge for the segment between village i and village i+1 being open for one day. Next m lines, each line contains two integers  ai,bi(1ai,bi<=n,ai!=bi) .
 

Output
For each test case, output m lines, each line contains the charge for the i-th day.
 

Sample Input
  
  
4 3 1 2 3 1 3 3 4 2 4
 

Sample Output
  
  
3 5 5
 

Author
BUPT
 

Source
 

题意:有一条很长的路,上面有n个点,那么相邻的两个点之间有一段路,每段路有一个价值,也就是说n个点有n-1条路,然后给出 m 天的工作,每次工作给出 ai 和 bi 表示这一天 ai 到 bi 之间的路必须开通着,开通着需要花费价值,每条路只能被开启一次和关闭一次,什么时候开启和关闭自己决定,但是每开着的一天都有花费,要你求出每天的最小花费。

思路:只需要获得每段路的开始时间和关闭时间就ok了,但是数据比较大 O(n^2) 必然是不行的

我们把每一段路作为一个点,并赋予价值,然后我们离线处理给出的工作,首先我们从后往前,对给出的工作里的路段去更新它的开始时间,比如样例中,最后一天的工作为2 到 4,也就是第二段和第三段路,我们就可以将第二段和第三段路的开始时间更新为 3 (表示第三天开始),然后继续相前,就能得到最优的开始时间,然后拿动态数组Begin存下每个点的开始时间,然后同样的操作从前往后更新结束时间,存在End数组里。

处理完后,从第一天开始,在Begin数组里把第一天开启的路线的价值加到ans里,然后输出ans就是第一天的花费。

然后第一天结束,如果第一天有需要关闭的道路,那就减去它的价值,就这样一直处理到尾

ps:个人感觉其实不用线段树,直接拿两个数组出来也是可以在时间内统计出所有开始和结束时间的

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<vector>
using namespace std;
#define maxn 200005
int w[maxn],l[maxn],r[maxn];
vector<int>Begin[maxn],End[maxn];
struct node{
	int l,r,v;
}tree[maxn * 4];
int a[maxn];

void build(int i,int l,int r){
	tree[i].l = l;
	tree[i].r = r;
	tree[i].v = -1;
	if(l == r)
		return ;
	int mid = (l + r) >> 1;
	build(i << 1,l,mid);
	build(i << 1 | 1,mid + 1,r); 
}

void update(int i,int l,int r,int val){ //这是单点更新 
	if(l <= tree[i].l && r >= tree[i].r){
		tree[i].v = val;
		return ;
	}
	if(tree[i].v != -1){	//直接更新到根节点就好 
		tree[i << 1].v = tree[i << 1 | 1].v = tree[i].v;
		tree[i].v = -1;
	}
	int mid = (tree[i].l + tree[i].r) >> 1;
	if(l <= mid)
		update(i << 1,l,r,val);
	if(r > mid)
		update(i << 1 | 1,l,r,val);
}

int query(int i,int pos){
	if(tree[i].l == tree[i].r)
		return tree[i].v;
	if(tree[i].v != -1){
		return tree[i].v;
	}
	int mid = (tree[i].l + tree[i].r) >> 1;
	if(pos <= mid)
		return query(i << 1,pos);
	else
		return query(i << 1 | 1,pos);
}
int main(){
	int n,m;
	while(scanf("%d %d",&n,&m) != EOF){
		for(int i = 1;i < n;i++){
			scanf("%d",&w[i]);	//输入每段路价值 
		}
		for(int i = 1;i <= m;i++){
			Begin[i].clear();	//情况动态数组 
			End[i].clear();
			scanf("%d %d",&l[i],&r[i]);
			if(l[i] > r[i])  	//保证左比右小 
				swap(l[i],r[i]);
		}
		build(1,1,n - 1); 	// 清空线段树 
		for(int i = m;i >= 1;i--){
			update(1,l[i],r[i] - 1,i); //从后往前更新每段路的开始日期 
		}
		for(int i = 1;i < n;i++){
			int day = query(1,i);	//更新完之后查询每段路的开始时间做个记录 
			if(day != -1)
				Begin[day].push_back(i);
		}
		build(1,1,n - 1);	//清空 
		for(int i = 1;i <= m;i++)	//从前往后更新每段路的结束日期 
			update(1,l[i],r[i] - 1,i);
		for(int i = 1;i < n;i++){
			int day = query(1,i);	//记录 
			if(day != -1)
				End[day].push_back(i);
		}
		int ans = 0;
		for(int i = 1;i <= m;i++){	//一天天来 
			int len = Begin[i].size();
			for(int j = 0;j < len;j++){
				int x = Begin[i][j]; //在今天开始的路把价值加上 
				ans += w[x];
			}
			printf("%d\n",ans);	//输入今天的价值 
			len = End[i].size();
			for(int j = 0;j < len;j++){	//如果今天有关闭的路,那么减掉它们的价值 
				int x = End[i][j];
				ans -= w[x];
			}
		}
	}
	return 0;
}


  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值