hdu 5861Road(2016 Multi-University Training Contest 10——线段树+扫描线)

题目链接:http://acm.split.hdu.edu.cn/showproblem.php?pid=5861

Road

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


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-1条路。每条路开放一天的价格是w[i],有m天的操作,每天需要用到村子a[i]到b[i]之间的道路。

每条路只能开放或者关闭一次(不能重复开关)要确保每一个运输都要完成,求每天最小的花费。


解题思路:

每段路只能打开一次,关闭一次,我们可以求出第i 段路最早什么时候使用是s[i].day,最晚什么时候使用ss[i].day,那么我们就可以在天数s[i].day 打开它,ss[i].day+1关闭它。

遍历m 天,对于每一天,进行完打开和关闭操作后,输出总和就好。

得到第i段开始和结束在哪一天之后,要求出每一天的花费,我们就采用扫描线的方法,在开始的那天+w[i],结束的那天的后一天减w[i]。接着不断地后面加上前面的即可得到每段的花费。


详见代码。

#include <iostream>
#include <cstdio>
#include <cstring>

using namespace std;

#define N (200000+10)

struct node
{
    int l,r,day;
    int flag;//用来标记能不能更新
} s[N*4];

struct node1
{
    int l,r,day;
    int flag;
} ss[N*4];

int w[N],a[N],b[N],st[N],ed[N],ans[N];

void InitTree(int l,int r,int k)
{
    s[k].l=l;
    s[k].r=r;
    s[k].day=0;
    s[k].flag=0;
    ss[k].l=l;
    ss[k].r=r;
    ss[k].day=0;
    ss[k].flag=0;
    if (l==r)
        return ;
    int mid=(l+r)/2;
    InitTree(l,mid,2*k);
    InitTree(mid+1,r,2*k+1);
}

void UpdataTree(int l,int r,int k,int d)
{
    if (s[k].day) return;
    if (s[k].l==l&&s[k].r==r&&s[k].flag==0)
    {
        s[k].day=d;//第一次用这条路是哪一天
        s[k].flag=1;
        return ;
    }
    if (s[k].l==s[k].r)
        return ;
    int mid=(s[k].l+s[k].r)/2;
    if (r<=mid)
        UpdataTree(l,r,2*k,d);
    else if (l>mid)
        UpdataTree(l,r,2*k+1,d);
    else
    {
        UpdataTree(l,mid,2*k,d);
        UpdataTree(mid+1,r,2*k+1,d);
    }
    s[k].flag=s[2*k].flag|s[2*k+1].flag;
}

void UpdataTree1(int l,int r,int k,int d)
{
    if (ss[k].day) return;
    if (ss[k].l==l&&ss[k].r==r&&ss[k].flag==0)
    {
        ss[k].day=d;//最后一次用这条路是哪一天
        ss[k].flag=1;
        return ;
    }
    if (ss[k].l==ss[k].r)
        return ;
    int mid=(ss[k].l+ss[k].r)/2;
    if (r<=mid)
        UpdataTree1(l,r,2*k,d);
    else if (l>mid)
        UpdataTree1(l,r,2*k+1,d);
    else
    {
        UpdataTree1(l,mid,2*k,d);
        UpdataTree1(mid+1,r,2*k+1,d);
    }
    ss[k].flag=ss[2*k].flag|ss[2*k+1].flag;
}

int SearchTree(int l,int r,int k)
{
    if (s[k].day) return s[k].day;
    if (s[k].l==l&&s[k].r==r)
        return s[k].day;
    int mid=(s[k].l+s[k].r)/2;
    if (r<=mid)
        SearchTree(l,r,2*k);
    else if (l>mid)
        SearchTree(l,r,2*k+1);
    else
    {
        SearchTree(l,mid,2*k);
        SearchTree(mid+1,r,2*k+1);
    }
}

int SearchTree1(int l,int r,int k)
{
    if (ss[k].day) return ss[k].day;
    if (ss[k].l==l&&ss[k].r==r)
        return ss[k].day;
    int mid=(ss[k].l+ss[k].r)/2;
    if (r<=mid)
        SearchTree1(l,r,2*k);
    else if (l>mid)
        SearchTree1(l,r,2*k+1);
    else
    {
        SearchTree1(l,mid,2*k);
        SearchTree1(mid+1,r,2*k+1);
    }
}

int main()
{
    int n,m;
    while (~scanf("%d%d",&n,&m))
    {
        for (int i=1; i<n; i++)
        {
            scanf("%d",&w[i]);
        }
        InitTree(1,n-1,1);
        for (int i=1; i<=m; i++)
        {
            scanf("%d%d",&a[i],&b[i]);
            if (a[i]>b[i])
                swap(a[i],b[i]);
            UpdataTree(a[i],b[i]-1,1,i);
        }
        for (int i=1; i<n; i++)
            st[i]=SearchTree(i,i,1);

        for (int i=m; i>=1; i--)
        {
            UpdataTree1(a[i],b[i]-1,1,i);
        }
        for (int i=1; i<n; i++)
            ed[i]=SearchTree1(i,i,1);
        //现在已知第i段的开始和结束分别在哪一天,求每一天需要花多少钱
        //扫描线的方法
        //for (int i=1; i<n; i++)
        //    cout<<i<<" "<<st[i]<<" "<<ed[i]<<endl;
        memset(ans,0,sizeof(ans));
        for (int i=1; i<n; i++)
        {
            ans[st[i]]+=w[i];
            ans[ed[i]+1]-=w[i];
        }
        for (int i=1; i<=m; i++)
        {
            ans[i]+=ans[i-1];
            printf ("%d\n",ans[i]);
        }
    }
    return 0;
}




  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值