HDU 5861 (思维+线段树区间更新+单点查询)

Road

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


 

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(1≤ai,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

2016 Multi-University Training Contest 10

 题意:有n个村庄,每相邻两个村庄之间的路有一个费用,有m个查询,代表m天每天的路程计划从L走到R,每天所需要的最小费用, 保证每一天l走到r时所有需要的路都打开,每条路只能打开和关闭一次。

题解:因为只能打开和关闭一次,所以我们线段树维护每条路最早的打开时间和最晚的关闭时间,那么对于每一个查询的l-r,可以用线段树区间赋值i,表示第几天开启,并且更新天数的最大最小值。nlogn可以解决第一个问题。那么对于每一天,我们维护一个当天需要开启的路和需要关闭的路,直接单点查询就OK,对于每一个查询都可以由前一个查询递推过来。

实现代码:

#include<bits/stdc++.h>
using namespace std;
int node[200010*4];
vector<int>in[200010],out[200010];
int w[200010];
int l[200010],r[200010];
void update(int p,int q,int l,int r,int n,int x)//用区间更新来给[p,q]这个区间更新一个值,这个值就是第i天的i;
{
    
  if(p<=l&&q>=r)
  {
    node[n]=x;
    return;
  }
  if(node[n]!=-1)
  {
    node[n<<1]=node[n<<1|1]=node[n];
    node[n]=-1;
  }
  int mid=(l+r)>>1;
  if(p>mid)update(p,q,mid+1,r,n<<1|1,x);
  else if(q<=mid)update(p,q,l,mid,n<<1,x);
  else
  {
    update(p,q,mid+1,r,n<<1|1,x);
    update(p,q,l,mid,n<<1,x);
  }
}
int query(int l,int r,int n,int x)
{
  if(l==r) return node[n];
  if(node[n]!=-1) return node[n];
  int mid=(l+r)>>1;
  if(x<=mid)return query(l,mid,n<<1,x);
  else return query(mid+1,r,n<<1|1,x);
}
int main()
{
  int n,m;
  while(scanf("%d%d",&n,&m)!=EOF)
  {
    for(int i=0; i<=m; i++)
    {
      in[i].clear();
      out[i].clear();
    }
    memset(node,-1,sizeof(node));
    for(int i=1; i<n; i++)scanf("%d",&w[i]);
    for(int i=1; i<=m; i++)
    {
      scanf("%d%d",&l[i],&r[i]);
      if(l[i]>r[i]) swap(l[i],r[i]);
    }
    for(int i=m; i>=1; i--)update(l[i],r[i]-1,1,n,1,i); //用区间更新来给[a,b]这个区间更新一个值
    //这个值就是第i天的i;
    for(int i=1; i<n; i++) //m次更新后,每一段都对应了一个打开时间;
    {
      int tmp=query(1,n,1,i);//再对每一段单点查询 ,这段对应的是第几天
      if(tmp!=-1)in[tmp].push_back(i);//第i天需要开启的线路的费用
    }
    memset(node,-1,sizeof(node));
    for(int i=1; i<=m; i++)update(l[i],r[i]-1,1,n,1,i);
    //m次更新后,每一段都对应了一个关闭时间
    for(int i=1; i<n; i++)
    {
      int tmp=query(1,n,1,i);//再对每一段单点查询 ,这段对应的是第几天
     if(tmp!=-1)out[tmp].push_back(i);
    }
    int ans=0;
    for(int i=1; i<=m; i++)//每一天
    {
      for(int j=0; j<in[i].size(); j++)ans+=w[in[i][j]];//需要开启的线路的费用
      printf("%d\n",ans);
      for(int j=0; j<out[i].size(); j++)ans-=w[out[i][j]];//可以关闭的线路的费用
    }
  }
  return 0;
}

猜你喜欢

转载自blog.csdn.net/qq_41668093/article/details/83217086