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
  • 相关阅读:
    Ubuntu 服务器默认的root账号是没有激活的,需要用初装的用户账号给root设置管理密码
    MySQL忘记root密码重置密码(5.7版本)
    SpringMvc与前台ajax数据传递
    将http://localhost:8080设置为项目主页
    javaweb项目主页设置
    Redis在java开发中使用
    eclipse基于git上传项目到码云上
    spring配置tomcat jdbc pool数据库连接池
    run as maven build时报错
    Tomcat-Jdbc-Pool连接池参数说明
  • 原文地址:https://www.cnblogs.com/asdfsag/p/10773927.html
Copyright © 2011-2022 走看看