zoj 3724 Delivery 离线处理+线段树

  1 #include <iostream>
  2 #include <cstdio>
  3 #include <algorithm>
  4 #include <cstring>
  5 #define ll long long
  6 using namespace std;
  7 int n,m,t,tol,tt;
  8 ll f[100010];
  9 ll sum[100010];
 10 struct edge{
 11     int u,v,x;
 12     ll l;
 13     edge(){}
 14     edge(int u,int v,ll l,int x):u(u),v(v),l(l),x(x){}
 15     friend bool operator<(edge a,edge b){
 16         if(a.u!=b.u) return a.u>b.u;
 17         else if(a.v!=b.v) return a.v<b.v;
 18         else return a.x<b.x;
 19     }
 20 }e[800010],te[800010];
 21 struct tree{
 22     int l,r;
 23     ll maxc,lz;
 24 }a[800010];
 25 ll ans[200010];
 26 void build(int l,int r,int k){
 27     a[k].l=l,a[k].r=r;
 28     a[k].maxc=a[k].lz=0;
 29     if(l<r){
 30        int mid=(l+r)>>1;
 31        build(l,mid,k<<1);
 32        build(mid+1,r,k<<1|1);
 33     }
 34 }
 35 void pushdown(int k){
 36     if(a[k].lz!=0){
 37        a[k<<1].maxc=max(a[k].lz,a[k<<1].maxc);
 38        a[k<<1|1].maxc=max(a[k].lz,a[k<<1|1].maxc);
 39        a[k<<1].lz=max(a[k<<1].lz,a[k].lz);
 40        a[k<<1|1].lz=max(a[k<<1|1].lz,a[k].lz);
 41        a[k].lz=0;
 42     }
 43 }
 44 void insert(int l,int r,ll c,int k){
 45     if(l<=a[k].l&&a[k].r<=r){
 46         a[k].maxc=max(a[k].maxc,c);
 47         a[k].lz=max(a[k].lz,c);
 48     }else{
 49         pushdown(k);
 50         int mid=(a[k].l+a[k].r)>>1;
 51         if(r<=mid) insert(l,r,c,k<<1);
 52         else if(l>mid) insert(l,r,c,k<<1|1);
 53         else insert(l,mid,c,k<<1),insert(mid+1,r,c,k<<1|1);
 54         a[k].maxc=max(a[k<<1].maxc,a[k<<1|1].maxc);
 55     }
 56 }
 57 ll query(int l,int r,int k){
 58     if(l<=a[k].l&&a[k].r<=r){
 59         return a[k].maxc;
 60     }else{
 61         pushdown(k);
 62         int mid=(a[k].l+a[k].r)>>1;
 63         if(r<=mid) return query(l,r,k<<1);
 64         else if(l>mid) return query(l,r,k<<1|1);
 65         else return max(query(l,mid,k<<1),query(mid+1,r,k<<1|1));
 66     }
 67 }
 68 void read(){
 69     for(int i=1;i<n;i++) scanf("%lld",&f[i]);
 70     int u,v;
 71     ll l;
 72     tol=tt=0;
 73     f[n]=(ll)1e12;
 74     for(int i=0;i<m;i++){
 75        scanf("%d%d%lld",&u,&v,&l);
 76        if(u==v) continue;
 77        if(u==n&&v==1) f[n]=min(f[n],l);
 78        if(u<v) e[tol++]=edge(u,v,l,0);
 79        else te[tt++]=edge(u,v+n,l,0);
 80     }
 81     scanf("%d",&t);
 82     for(int i=0;i<t;i++){
 83        scanf("%d%d",&u,&v);
 84        if(u<=v) e[tol++]=edge(u,v,(ll)i,1);
 85        else te[tt++]=edge(u,v+n,(ll)i,1);
 86     }
 87 }
 88 void gao(){
 89     sum[0]=0;
 90     for(int i=1;i<n;i++) sum[i]=sum[i-1]+f[i];
 91     sort(e,e+tol);
 92     build(1,n,1);
 93     for(int i=0;i<tol;i++){
 94         if(e[i].x==0){
 95             insert(e[i].v,n,sum[e[i].v-1]-sum[e[i].u-1]-e[i].l,1);
 96         }else{
 97             ans[e[i].l]=sum[e[i].v-1]-sum[e[i].u-1]-query(e[i].u,e[i].v,1);
 98         }
 99     }
100     sort(te,te+tt);
101     build(1,2*n-1,1);
102     for(int i=0;i<tt;i++){
103         int u=te[i].u,v=te[i].v-n;
104         if(te[i].x==0){
105             insert(v+n,2*n-1,f[n]+sum[n-1]-sum[u-1]+sum[v-1]-te[i].l,1);
106         }else{
107             ans[te[i].l]=f[n]+sum[n-1]-sum[u-1]+sum[v-1]-query(u,v+n,1);
108         }
109     }
110 }
111 void out(){
112     for(int i=0;i<t;i++){
113         printf("%lld
",ans[i]);
114     }
115 }
116 int main(){
117     while(~scanf("%d%d",&n,&m)){
118         read();
119         gao();
120         out();
121     }
122     return 0;
123 }
zoj3724

解题思路:

由于题目中说短边只会走一次,所以可以发现,往前和往后是不影响的,可以分开讨论。

然后线段树中记录的走短边的话最多可以省多少,

离线处理,将修改和询问放一起,先按u从大到小,再按v从小到大排序,以上都一样时,修改优先。

当u<=v时,插入的线段是[v,n],大小置为u到v的原长度-短边,询问雷同。

当u>v时,我的做法是将线段树延伸到2*n-1处,那么线段就变为[u,v+n],那么排序和插入的做法和上面一样。

原文地址:https://www.cnblogs.com/wonderzy/p/3407646.html