zoukankan      html  css  js  c++  java
  • HDU

    题目链接

    题意:n个人排成一列,一开始他们互不认识,每次选[l,r]上的人开party,使他们互相认识,求出每次party之后新互相认识的人的对数。

    思路:把“互相认识”变成单向连边,只考虑左边的人对右边的贡献。对于每个人,他认识的人的区间必然是连续的,可以维护他认识的最右边的人R,这样更新操作相当于把[l,r]所有人的R值变成max(R,r),可以构造线段树维护每个区间中R的最小值mi,如果最小值大于等于R的话就不用更新了,直接退出,否则暴力修改每个点的值。

    先上个假算法:

     1 #include<bits/stdc++.h>
     2 using namespace std;
     3 typedef long long ll;
     4 const int N=5e5+10,inf=0x3f3f3f3f;
     5 #define ls (u<<1)
     6 #define rs (u<<1|1)
     7 #define mid ((l+r)>>1)
     8 int mi[N<<2],n,m;
     9 ll ans;
    10 void pu(int u) {mi[u]=min(mi[ls],mi[rs]);}
    11 void build(int u=1,int l=1,int r=n) {
    12     if(l==r) {mi[u]=l; return;}
    13     build(ls,l,mid),build(rs,mid+1,r),pu(u);
    14 }
    15 void upd(int L,int R,int u=1,int l=1,int r=n) {
    16     if(l>R||r<L||mi[u]>=R)return;
    17     if(l==r) {ans+=R-mi[u],mi[u]=R; return;}
    18     upd(L,R,ls,l,mid),upd(L,R,rs,mid+1,r),pu(u);
    19 }
    20 int main() {
    21     while(scanf("%d%d",&n,&m)==2) {
    22         build();
    23         while(m--) {
    24             ans=0;
    25             int l,r;
    26             scanf("%d%d",&l,&r);
    27             upd(l,r);
    28             printf("%lld
    ",ans);
    29         }
    30     }
    31     return 0;
    32 }
    View Code

    这个算法本身是没有问题的,交上去也能AC,但会被一些极端的数据卡死,比如[1,1],[1,2],...,[1,n]这样的,会被卡成n^2,因此可以加一些优化。

    由于每个人认识的最右边的人R的值是非递减的,即任意i>j,R[i]>=R[j],因此每次发生变化的区间必然是连续的,可以把单点修改换成区间修改,这样就不会被卡了。

     1 #include<bits/stdc++.h>
     2 using namespace std;
     3 typedef long long ll;
     4 const int N=5e5+10,inf=0x3f3f3f3f;
     5 #define ls (u<<1)
     6 #define rs (u<<1|1)
     7 #define mid ((l+r)>>1)
     8 int mx[N<<2],mi[N<<2],lz[N<<2],n,m;
     9 ll sum[N<<2],ans;
    10 void pu(int u) {
    11     mi[u]=min(mi[ls],mi[rs]);
    12     mx[u]=max(mx[ls],mx[rs]);
    13     sum[u]=sum[ls]+sum[rs];
    14 }
    15 void pd(int u,int l,int r) {
    16     if(lz[u]) {
    17         sum[ls]=(ll)lz[u]*(mid-l+1),sum[rs]=(ll)lz[u]*(r-mid);
    18         mi[ls]=mi[rs]=mx[ls]=mx[rs]=lz[ls]=lz[rs]=lz[u],lz[u]=0;
    19     }
    20 }
    21 void build(int u=1,int l=1,int r=n) {
    22     lz[u]=0;
    23     if(l==r) {sum[u]=mi[u]=mx[u]=l; return;}
    24     build(ls,l,mid),build(rs,mid+1,r),pu(u);
    25 }
    26 void upd(int L,int R,int u=1,int l=1,int r=n) {
    27     if(l>R||r<L||mi[u]>=R)return;
    28     if(l>=L&&r<=R&&mx[u]<=R) {sum[u]=(ll)R*(r-l+1),mi[u]=mx[u]=lz[u]=R; return;}
    29     pd(u,l,r);
    30     upd(L,R,ls,l,mid),upd(L,R,rs,mid+1,r),pu(u);
    31 }
    32 int main() {
    33     while(scanf("%d%d",&n,&m)==2) {
    34         build(),ans=sum[1];
    35         while(m--) {
    36             int l,r;
    37             scanf("%d%d",&l,&r);
    38             upd(l,r);
    39             printf("%lld
    ",sum[1]-ans);
    40             ans=sum[1];
    41         }
    42     }
    43     return 0;
    44 }
    View Code

    然后据说还有一种叫“吉司机线段树”的东西也能做?赶紧学了学(便乘),感觉对于区间取max/min这类问题的处理强大的,普适性也比较高。

    对于区间取max操作,其基本思想是维护区间和sum,区间最小值mi,区间次小值se以及区间最小值个数nmi。如果要对[l,r]上的所有数与x取max,那么分三种情况讨论即可:

    1)若x<=mi,则修改操作无效,退出

    2)若mi<x<se,则将mi改成x,(sum+=x-mi)*ni,其余不变,同时下放标记

    3)若x>=se,则在左右区间递归进行下去

     1 #include<bits/stdc++.h>
     2 using namespace std;
     3 typedef long long ll;
     4 const int N=5e5+10,inf=0x3f3f3f3f;
     5 #define ls (u<<1)
     6 #define rs (u<<1|1)
     7 #define mid ((l+r)>>1)
     8 int mi[N<<2],nmi[N<<2],se[N<<2],lz[N<<2],n,m;
     9 ll sum[N<<2],ans;
    10 void pu(int u) {
    11     sum[u]=sum[ls]+sum[rs];
    12     mi[u]=min(mi[ls],mi[rs]),se[u]=max(mi[ls],mi[rs]);
    13     se[u]=se[u]==mi[u]?min(se[ls],se[rs]):min(se[u],min(se[ls],se[rs]));
    14     nmi[u]=(mi[ls]==mi[u]?nmi[ls]:0)+(mi[rs]==mi[u]?nmi[rs]:0);
    15 }
    16 void change(int u,int x) {sum[u]+=(ll)nmi[u]*(x-mi[u]),mi[u]=lz[u]=x;}
    17 void pd(int u) {
    18     if(~lz[u]) {
    19         if(mi[ls]<lz[u])change(ls,lz[u]);
    20         if(mi[rs]<lz[u])change(rs,lz[u]);
    21         lz[u]=-1;
    22     }
    23 }
    24 void build(int u=1,int l=1,int r=n) {
    25     lz[u]=-1;
    26     if(l==r) {sum[u]=mi[u]=l,nmi[u]=1,se[u]=inf; return;}
    27     build(ls,l,mid),build(rs,mid+1,r),pu(u);
    28 }
    29 void upd(int L,int R,int x,int u=1,int l=1,int r=n) {
    30     if(l>R||r<L||x<=mi[u])return;
    31     if(l>=L&&r<=R&&x<se[u]) {change(u,x); return;}
    32     pd(u),upd(L,R,x,ls,l,mid),upd(L,R,x,rs,mid+1,r),pu(u);
    33 }
    34 int main() {
    35     while(scanf("%d%d",&n,&m)==2) {
    36         build(),ans=sum[1];
    37         while(m--) {
    38             int l,r;
    39             scanf("%d%d",&l,&r);
    40             upd(l,r,r);
    41             printf("%lld
    ",sum[1]-ans);
    42             ans=sum[1];
    43         }
    44     }
    45     return 0;
    46 }
    View Code
  • 相关阅读:
    算法训练 表达式计算
    基础练习 十六进制转十进制
    基础练习 十六进制转十进制
    基础练习 十六进制转十进制
    New ways to verify that Multipath TCP works through your network
    TCP的拥塞控制 (Tahoe Reno NewReno SACK)
    Multipath TCP Port for Android 4.1.2
    How to enable ping response in windows 7?
    NS3
    Multipath TCP Port for Android
  • 原文地址:https://www.cnblogs.com/asdfsag/p/10773927.html
Copyright © 2011-2022 走看看