• 线段树专题


    搞完了线段树专题

    拿几道还比较有意义的题来写一下博客吧

    还是先%棒神为好

    ① 小白逛公园 bzoj 1756

    题目大意:

    一个序列 支持单点修改和区间内连续子段最大值

    思路:

    对于线段树上每一段维护四个东西

    每一段从左起的最大值 从右起的最大值 这一段的sum 这一段的答案

    然后合并的时候可以很容易的合并 此时在query的时候return 一个节点比较方便

     1 #include<iostream>
     2 #include<cstdio>
     3 #include<cstring>
     4 #include<cstdlib>
     5 #include<cmath>
     6 #include<algorithm>
     7 #include<queue>
     8 #include<vector>
     9 #include<map>
    10 #define inf 2139062143
    11 #define ll long long
    12 #define MAXN 500100
    13 using namespace std;
    14 inline int read()
    15 {
    16     int x=0,f=1;char ch=getchar();
    17     while(!isdigit(ch)) {if(ch=='-') f=-1;ch=getchar();}
    18     while(isdigit(ch)) x=x*10+ch-'0',ch=getchar();
    19     return x*f;
    20 }
    21 struct data 
    22 {
    23     int mx,lm,rm,sum,l,r;
    24     //void PRINT() {cout<<l<<" "<<r<<" "<<mx<<" "<<lm<<" "<<rm<<" "<<sum<<endl;}
    25 }tr[MAXN<<2];
    26 int n,T,a[MAXN];
    27 void upd(int k)
    28 {
    29     tr[k].sum=tr[k<<1].sum+tr[k<<1|1].sum;
    30     tr[k].lm=max(tr[k<<1].lm,tr[k<<1|1].lm+tr[k<<1].sum);
    31     tr[k].rm=max(tr[k<<1|1].rm,tr[k<<1].rm+tr[k<<1|1].sum);
    32     tr[k].mx=max(tr[k<<1].rm+tr[k<<1|1].lm,max(tr[k<<1|1].mx,tr[k<<1].mx));
    33     //tr[k].PRINT();
    34 }
    35 void build(int k,int l,int r)
    36 {
    37     tr[k].l=l,tr[k].r=r;
    38     if(l==r) {tr[k].mx=tr[k].lm=tr[k].rm=tr[k].sum=a[l];return ;}
    39     int mid=(l+r)>>1;
    40     build(k<<1,l,mid);build(k<<1|1,mid+1,r);
    41     upd(k);
    42 }
    43 void mdf(int k,int x,int w)
    44 {
    45     int l=tr[k].l,r=tr[k].r;
    46     if(l==r) {tr[k].mx=tr[k].lm=tr[k].rm=tr[k].sum=w;return ;}
    47     int mid=(l+r)>>1;
    48     if(mid>=x) mdf(k<<1,x,w);
    49     else mdf(k<<1|1,x,w);
    50     //cout<<"MODIFY: "<<k<<" "<<x<<" "<<w<<endl;
    51     upd(k);
    52 }
    53 data query(int k,int a,int b)
    54 {
    55     int l=tr[k].l,r=tr[k].r;
    56     if(l==a&&r==b) return tr[k];
    57     int mid=(l+r)>>1;
    58     if(mid>=b) return query(k<<1,a,b);
    59     else if(mid<a) return query(k<<1|1,a,b);
    60     else
    61     {
    62         //cout<<"Q: "<<k<<" "<<a<<" "<<b<<endl;
    63         data x=query(k<<1,a,mid),y=query(k<<1|1,mid+1,b),res;
    64         //x.PRINT();y.PRINT();
    65         res.lm=max(x.lm,y.lm+x.sum);
    66         res.rm=max(y.rm,x.rm+y.sum);
    67         res.mx=max(x.rm+y.lm,max(y.mx,x.mx));
    68         //res.PRINT();
    69         return res;
    70     }
    71 }
    72 int main()
    73 {
    74     n=read(),T=read();int x,y,z;data res;
    75     for(int i=1;i<=n;i++) a[i]=read();
    76     build(1,1,n);
    77     while(T--)
    78     {
    79         x=read(),y=read(),z=read();
    80         if(x==1) printf("%d
    ",query(1,min(y,z),max(y,z)).mx);
    81         else mdf(1,y,z);
    82     }
    83 }
    View Code

    ② MooFest poj 1990

    题目大意:

    现在有n头奶牛,第i头奶牛的听力阈值为v(i),即如果其他奶牛想让这只奶牛听到它的声音,那么至少得发出v(i)乘上它们之间距离这么大的音量

    所以如果第i头奶牛和第j头奶牛交谈,那么发出的音量大小至少是它们之间的距离乘上它们两个阈值中的最大值,即max(v(i),v(j))

    现在这n头奶牛站在了一条直线上,每头奶牛都站在唯一的坐标上

    并且每对奶牛交谈都尽可能降低音量。那么请你计算出这n*(n-1)/2对奶牛交谈产生的最小总音量大小

    思路:

    使用树状数组就可以很方便的维护

    先将奶牛按照v值排序 这样它与排序后前面的奶牛说话使用的是它的v值

    然后我们需要求出它前面所有奶牛与它的距离之和

    可以分成两种情况 位置在左边或者右边

    然后我们开两个树状数组分别维护哪个位置有奶牛来求出它左边和右边各有多少个奶牛

    另一个维护距离之和 这样就可以分别知道它左边和右边奶牛的距离之和 然后推一个小学数学公式即可

     1 #include<iostream>
     2 #include<cstdio>
     3 #include<cstring>
     4 #include<cstdlib>
     5 #include<cmath>
     6 #include<algorithm>
     7 #include<queue>
     8 #include<vector>
     9 #include<map>
    10 #define inf 2139062143
    11 #define ll long long
    12 #define MAXN 20100
    13 using namespace std;
    14 inline int read()
    15 {
    16     int x=0,f=1;char ch=getchar();
    17     while(!isdigit(ch)) {if(ch=='-') f=-1;ch=getchar();}
    18     while(isdigit(ch)) x=x*10+ch-'0',ch=getchar();
    19     return x*f;
    20 }
    21 int n;
    22 ll ans;
    23 struct data {int pos,val;}g[MAXN];
    24 bool cmp(data x,data y) {return x.val<y.val;}
    25 struct array
    26 {
    27     ll c[MAXN];
    28     inline int lowbit(int x) {return x&(-x);}
    29     void add(int x,int val) {for(int i=x;i<=MAXN;i+=lowbit(i)) c[i]+=val;}
    30     ll sum(int x) {ll res=0;for(int i=x;i;i-=lowbit(i)) res+=c[i];return res;}
    31 }a,b;
    32 int main()
    33 {
    34     n=read();ll tot=0,x,y;
    35     for(int i=1;i<=n;i++) g[i].val=read(),g[i].pos=read();
    36     sort(g+1,g+n+1,cmp);
    37     for(int i=1;i<=n;i++)
    38     {
    39         x=a.sum(g[i].pos-1),y=b.sum(g[i].pos-1);
    40         ans+=g[i].val*(x*g[i].pos-y-(i-x-1)*g[i].pos+(tot-b.sum(g[i].pos-1)));
    41         //cout<<g[i].val<<"*("<<x<<"*"<<g[i].pos<<"-"<<y<<"-"<<(i-x-1)<<"*"<<g[i].pos<<"+("<<tot<<"-"<<b.sum(g[i].pos-1)<<"))"<<endl<<ans<<endl;
    42         tot+=g[i].pos;
    43         a.add(g[i].pos,1);b.add(g[i].pos,g[i].pos);
    44     }
    45     printf("%lld",ans);
    46 }
    View Code

    ③ 遭遇战 vijos 1404 

    题目大意:

    一个大区间1-n

    有一些区间l-r可以覆盖并有一些代价

    求出最小代价覆盖s-t区间

    思路:

    可以建图来跑最短路

    对于每个l r 从l向r+1连一条边 然后每个i向i-1连一条边

    最后跑s-t+1的最短路即可(dij比spfa快到不知道哪里去了

     1 #include<iostream>
     2 #include<cstdio>
     3 #include<cstring>
     4 #include<cstdlib>
     5 #include<cmath>
     6 #include<algorithm>
     7 #include<queue>
     8 #include<vector>
     9 #include<map>
    10 #define inf 2139062143
    11 #define ll long long
    12 #define MAXN 90101
    13 #define P pair<int,int>
    14 #define mp(a,b) make_pair(a,b)
    15 using namespace std;
    16 inline int read()
    17 {
    18     int x=0,f=1;char ch=getchar();
    19     while(!isdigit(ch)) {if(ch=='-') f=-1;ch=getchar();}
    20     while(isdigit(ch)) x=x*10+ch-'0',ch=getchar();
    21     return x*f;
    22 }
    23 int s,t,dis[MAXN],n;
    24 int fst[MAXN],nxt[MAXN<<1],to[MAXN<<1],val[MAXN<<1],cnt;
    25 void add(int u,int v,int w) {nxt[++cnt]=fst[u],fst[u]=cnt,to[cnt]=v,val[cnt]=w;}
    26 int dij()
    27 {
    28     priority_queue<P,vector<P>,greater<P> > q;
    29     memset(dis,127,sizeof(dis));
    30     dis[s]=0;q.push(mp(0,s));
    31     while(!q.empty())
    32     {
    33         int k=q.top().second;q.pop();
    34         for(int i=fst[k];i;i=nxt[i])
    35             if(dis[to[i]]>dis[k]+val[i]) {dis[to[i]]=dis[k]+val[i];q.push(mp(dis[to[i]],to[i]));}
    36     }
    37     return dis[t+1]==inf?-1:dis[t+1];
    38 }
    39 int main()
    40 {
    41     int a,b,c;
    42     n=read(),s=read(),t=read();
    43     while(n--) {a=max(read(),s),b=min(read()+1,t+1),c=read();add(a,b,c);add(b,a,c);}
    44     for(int i=s+1;i<=t+1;i++) add(i,i-1,0);
    45     printf("%d",dij());
    46 }
    View Code

    ④ Mayor's posters poj 2528

    题目大意:

    向一些区间上贴海报

    问贴完之后有多少个海报露出来

    思路:

    本来是一道区间修改+暴力查询的zz题

    但是因为数据较大

    需要离散化技巧

    对于每个距离>1的点 需要在离散时+1

    不然会有反例出现

     1 #include<iostream>
     2 #include<cstdio>
     3 #include<cmath>
     4 #include<cstdlib>
     5 #include<cstring>
     6 #include<algorithm>
     7 #include<vector>
     8 #include<queue>
     9 #define inf 2139062143
    10 #define ll long long
    11 #define MAXN 40100
    12 using namespace std;
    13 inline int read()
    14 {
    15     int x=0,f=1;char ch=getchar();
    16     while(!isdigit(ch)) {if(ch=='-') f=-1;ch=getchar();}
    17     while(isdigit(ch)) {x=x*10+ch-'0';ch=getchar();}
    18     return x*f;
    19 }
    20 int T,n,ar[MAXN],cnt,ans,vis[MAXN];
    21 struct data {int pos,val;}g[MAXN];
    22 bool cmp(data a,data b) {return a.val<b.val;}
    23 namespace seg_tree
    24 {
    25     int tag[MAXN<<2];
    26     void pshd(int k)
    27     {
    28         tag[k<<1]=tag[k<<1|1]=tag[k];
    29         tag[k]=0;
    30     }
    31     void mdf(int k,int a,int b,int l,int r,int x)
    32     {
    33         //cout<<a<<" "<<b<<" "<<l<<" "<<r<<" "<<x<<endl;
    34         if(l==a&&r==b) {tag[k]=x;return ;}
    35         int mid=(l+r)>>1;
    36         if(tag[k]) pshd(k);
    37         if(b<=mid) mdf(k<<1,a,b,l,mid,x);
    38         else if(a>mid) mdf(k<<1|1,a,b,mid+1,r,x);
    39         else {mdf(k<<1,a,mid,l,mid,x);mdf(k<<1|1,mid+1,b,mid+1,r,x);}
    40     }
    41     void query(int k,int l,int r)
    42     {
    43         //cout<<k<<" "<<l<<" "<<r<<" "<<tag[k]<<" "<<ans<<endl;
    44         if(tag[k])
    45         {
    46             if(!vis[tag[k]]) ans++,vis[tag[k]]=1;
    47             return ;
    48         }
    49         if(l==r) return ;
    50         int mid=(l+r)>>1;
    51         query(k<<1,l,mid);query(k<<1|1,mid+1,r);
    52     }
    53 }
    54 using namespace seg_tree;
    55 int main()
    56 {
    57     T=read();
    58     while(T--)
    59     {
    60         memset(vis,0,sizeof(vis));
    61         memset(tag,0,sizeof(tag));
    62         n=read(),cnt=ans=0;
    63         for(int i=1;i<=n;i++) g[i*2-1].val=read(),g[i*2].val=read(),g[i*2-1].pos=i*2-1,g[i*2].pos=i*2;
    64         sort(g+1,g+2*n+1,cmp);
    65         for(int i=1;i<=2*n;i++)
    66             if(g[i].val!=g[i-1].val) cnt+=1+(g[i].val-g[i-1].val>1),ar[g[i].pos]=cnt;
    67             else ar[g[i].pos]=cnt;
    68         //for(int i=1;i<=2*n;i++) cout<<ar[i]<<" ";
    69         for(int i=1;i<=n;i++) mdf(1,ar[i*2-1],ar[i*2],1,cnt,i);
    70         query(1,1,cnt);
    71         printf("%d
    ",ans);
    72     }
    73 }
    View Code

    ⑤ 酒店Hotel luogu 2864

    题目大意:

    第一行输入n,m ,n代表有n个房间,开始都为空房,m表示以下有m行操作,以下 每行先输入一个数 i ,表示一种操作

    若i为1,表示查询房间,再输入一个数x,表示在1--n 房间中找到长度为x的连续空房,输出连续x个房间中左端的房间号

    尽量让这个房间号最小,若找不到长度为x的连续空房,输出0

    若i为2,表示退房,再输入两个数 x,y 代表 房间号 x---x+y-1 退房,即让房间为空

    思路:

    与小白逛公园类似

    只不过这一次要维护每一段最多有多少个连续的0

     1 #include<iostream>
     2 #include<cstdio>
     3 #include<cmath>
     4 #include<cstdlib>
     5 #include<cstring>
     6 #include<algorithm>
     7 #include<vector>
     8 #include<queue>
     9 #define inf 2139062143
    10 #define ll long long
    11 #define MAXN 50100
    12 using namespace std;
    13 inline int read()
    14 {
    15     int x=0,f=1;char ch=getchar();
    16     while(!isdigit(ch)) {if(ch=='-') f=-1;ch=getchar();}
    17     while(isdigit(ch)) {x=x*10+ch-'0';ch=getchar();}
    18     return x*f;
    19 }
    20 int n,T;
    21 struct data {int lm,rm,tag,ans;}tr[MAXN<<2];
    22 void upd(int k,int l,int r)
    23 {
    24     int mid=(l+r)>>1;
    25     tr[k].lm= (tr[k<<1].lm==mid-l+1)?mid-l+1+tr[k<<1|1].lm:tr[k<<1].lm;
    26     tr[k].rm= (tr[k<<1|1].rm==r-mid)?r-mid+tr[k<<1].rm:tr[k<<1|1].rm;
    27     tr[k].ans= max(tr[k<<1].rm+tr[k<<1|1].lm,max(tr[k<<1].ans,tr[k<<1|1].ans));
    28 }
    29 void build(int k,int l,int r)
    30 {
    31     tr[k].lm=tr[k].rm=tr[k].ans=r-l+1,tr[k].tag=0;
    32     if(l==r) return ;
    33     int mid=(l+r)>>1;
    34     build(k<<1,l,mid);build(k<<1|1,mid+1,r);
    35 }
    36 void pshd(int k,int l,int r)
    37 {
    38     //cout<<"P : "<<k<<endl;
    39     int mid=(l+r)>>1;
    40     if(tr[k].tag&1) tr[k<<1].lm=tr[k<<1].rm=tr[k<<1].ans=mid-l+1,tr[k<<1|1].lm=tr[k<<1|1].rm=tr[k<<1|1].ans=r-mid;
    41     else tr[k<<1].lm=tr[k<<1].rm=tr[k<<1].ans=tr[k<<1|1].lm=tr[k<<1|1].rm=tr[k<<1|1].ans=0;
    42     tr[k<<1].tag=tr[k<<1|1].tag=tr[k].tag;
    43     tr[k].tag=0;
    44 }
    45 void mdf(int k,int a,int b,int x,int l,int r)
    46 {
    47     //cout<<"M : "<<k<<" "<<a<<" "<<b<<" "<<x<<" "<<l<<" "<<r<<" "<<tr[k].ans<<" "<<tr[k].tag<<endl;
    48     if(l==a&&b==r)
    49     {
    50         if(x&1) tr[k].tag=1,tr[k].lm=tr[k].rm=tr[k].ans=r-l+1;
    51         else tr[k].tag=2,tr[k].lm=tr[k].rm=tr[k].ans=0;
    52         return ;
    53     }
    54     int mid=(l+r)>>1;
    55     if(tr[k].tag) pshd(k,l,r);
    56     if(b<=mid) mdf(k<<1,a,b,x,l,mid);
    57     else if(a>mid) mdf(k<<1|1,a,b,x,mid+1,r);
    58     else {mdf(k<<1,a,mid,x,l,mid);mdf(k<<1|1,mid+1,b,x,mid+1,r);}
    59     upd(k,l,r);
    60 }
    61 int query(int k,int x,int l,int r)
    62 {
    63     //cout<<"Q : "<<k<<" "<<x<<" "<<l<<" "<<r<<" "<<tr[k].ans<<endl;
    64     if(tr[k].ans<x) return 0;
    65     int mid=(l+r)>>1;
    66     if(tr[k].tag) pshd(k,l,r);
    67     if(tr[k<<1].ans>=x) return query(k<<1,x,l,mid);
    68     else if(tr[k<<1].rm+tr[k<<1|1].lm>=x) return mid-tr[k<<1].rm+1;
    69     else return query(k<<1|1,x,mid+1,r);
    70 }
    71 int main()
    72 {
    73     n=read(),T=read();int a,b,c;
    74     build(1,1,n);
    75     while(T--)
    76     {
    77         a=read(),b=read();
    78         if(a&1) 
    79         {
    80             c=query(1,b,1,n);
    81             printf("%d
    ",c);
    82             if(c) mdf(1,c,c+b-1,2,1,n);
    83         }
    84         else 
    85         {
    86             c=read();
    87             mdf(1,b,b+c-1,1,1,n);
    88         }
    89     }
    90 }
    View Code
  • 相关阅读:
    新认识:SDF数据库
    捆绑(Bundle)
    VirtualBox-4.3.0启动报错及解决办法
    File.Exists(Application.StartupPath + \Settings\Settings.xml)
    C# [method Modifiers] abstract virtual override new
    360打补丁后系统无法启动的解决办法
    String.Compare 方法 (String, Int32, String, Int32, Int32)
    hdu4277 USACO ORZ
    Java实现 蓝桥杯 算法训练 最小乘积
    Java实现 蓝桥杯 算法训练 最小乘积
  • 原文地址:https://www.cnblogs.com/yyc-jack-0920/p/8468963.html
Copyright © 2020-2023  润新知