【线段树】牛客国庆day3(2018hncpc) E. Grid 线段树

Grid

这是跟平时不太一样的线段树。

这道题要用是要线段树记录区间(线段),然后判断  当前所记录的  所有线段  总共覆盖的长度,也就是说,线段可能会重叠,重叠的区间长度不能重复计算,要计算实际得到的  所有线段的长度。

想想 怎么记录? 跟平时一样的话,我大概就是,把线段分为单个单个点,然后把区间标记为1,然后计算的时候就,到了标记为1的区间,就直接ans+=r-l+1,但是,因为是分散的区间线段,搜索时间会大于平时。有点不足,也不知道有没有别的做法。

但是,这道题还不能用这种做法,因为区间长度是1e9,需要离散。

所以看了别人的做法:离散询问出现的点的坐标,然后,线段是[l,r],把线段树的[l,r]的值置为h[r]-h[l],这个也不是我想说的重点。

但是,离散了之后,mid和mid+1的值就断开了,这样子 两个区间的值不就不连续了吗?会缺少[h[mid],h[mid+1]]的值。

所以看了别人的做法:线段树的左右子树的区间是[l,mid] [mid,r],或者  更准确的说是,传递到左右子树的离散数组区间是[ h[l],h[mid] ]和[ h[mid],h[r] ] ,这样子就能解决~...了吗?有个小细节忘了说 线段区间[val[l],val[r]]的长度并不是真实的h[r]-h[l],在记录r的时候 h[r]的值实际是val[r+1],这样才满足区间的真实长度(r-l+1)嘛。

想无,记下来。

代码:

#include<bits/stdc++.h>
#include<tr1/unordered_map>
#define debug printf("!");
using namespace std;
typedef long long ll;
const int maxn=1e5+50;
const int mod=1e9+7;
const int inf=0x3f3f3f3f;

struct P{
    int ly;ll sum;
}tree1[maxn<<3],tree2[maxn<<3];

int h[maxn<<1],tot,nn,mm;
int val(int v)
{
    return lower_bound(h+1,h+1+tot,v)-h;
}


void down(int k,int L,int R,P tree[])
{
    int mid=(L+R)>>1;
    tree[k<<1].sum=h[mid]-h[L];
    tree[k<<1|1].sum=h[R]-h[mid];
    tree[k<<1].ly=1;
    tree[k<<1|1].ly=1;
    tree[k].ly=0;
}

void update(int k,int L,int R,int l,int r,P tree[])
{
    if(l==L&&R==r)
    {
        tree[k].sum=h[r]-h[l];
        tree[k].ly=1;
        return ;
    }
    if(tree[k].ly)down(k,L,R,tree);
    int mid=(L+R)>>1;
    if(r<=mid)update(k<<1,L,mid,l,r,tree);
    else if(l>=mid)update(k<<1|1,mid,R,l,r,tree);
    else
    {
        update(k<<1,L,mid,l,mid,tree);
        update(k<<1|1,mid,R,mid,r,tree);
    }
    tree[k].sum=tree[k<<1].sum+tree[k<<1|1].sum;
}

struct Q{
    int op,a,b;
}p[maxn];

int main()
{
    ll n,m;
    ll ans,t;
    int q,op,a,b;
    while(~scanf("%lld%lld%d",&n,&m,&q))
    {
        tot=0;
        for(int i=1;i<=q;i++)
        {
            scanf("%d%d%d",&p[i].op,&p[i].a,&p[i].b);
            h[++tot]=p[i].a;h[++tot]=p[i].b+1;
        }
        h[++tot]=n+1;h[++tot]=m+1;
        sort(h+1,h+1+tot);
        tot=unique(h+1,h+1+tot)-h-1;
        nn=val(n+1);mm=val(m+1);
        for(int i=1;i<=tot*4;i++)
        {
            tree1[i].sum=tree1[i].ly=0;
            tree2[i].sum=tree2[i].ly=0;
        }
        for(int i=1;i<=q;i++)
        {
            a=val(p[i].a);b=val(p[i].b+1);
            if(p[i].op==1)update(1,1,nn,a,b,tree1);
            else update(1,1,mm,a,b,tree2);
            ans=(n*m)-(tree1[1].sum*m+tree2[1].sum*n)+(tree1[1].sum*tree2[1].sum);
            if(tree1[1].sum==0)ans+=tree2[1].sum;
            else if(tree2[1].sum==0)ans+=tree1[1].sum;
            else ans++;
            printf("%lld
",ans);
        }
    }
}

但是!!!但是,更好的做法是:动态开点线段树

但是!!!但是,在牛客这样做会超内存。等国庆过后去别的oj补题。

#include<bits/stdc++.h>
#include<tr1/unordered_map>
#define debug printf("!");
using namespace std;
typedef long long ll;
const int maxn=1e5+5;
const int mod=1e9+7;
const int inf=0x3f3f3f3f;

int sum1[maxn<<3],L1[maxn<<3],R1[maxn<<3],cnt1,T1;
int sum2[maxn<<3],L2[maxn<<3],R2[maxn<<3],cnt2,T2;

void init()
{
    for(int i=0;i<=cnt1;i++)sum1[i]=L1[i]=R1[i]=0;
    for(int i=0;i<=cnt2;i++)sum2[i]=L2[i]=R2[i]=0;
    cnt1=cnt2=0;
}

void update1(int &rt,int l,int r,int tl,int tr)
{
    if(!rt)rt=++cnt1;
    if(sum1[rt]==r-l+1)return;
    if(tl<=l&&r<=tr)
    {
        sum1[rt]=r-l+1;return;
    }
    int mid=(l+r)>>1;
    if(tl<=mid)update1(L1[rt],l,mid,tl,tr);
    if(tr>mid)update1(R1[rt],mid+1,r,tl,tr);
    sum1[rt]=sum1[L1[rt]]+sum1[R1[rt]];
}
void update2(int &rt,int l,int r,int tl,int tr)
{
    if(!rt)rt=++cnt2;
    if(sum2[rt]==r-l+1)return;
    if(tr<=l&&r<=tr)
    {
        sum2[rt]=r-l+1;return;
    }
    int mid=(l+r)>>1;
    if(tl<=mid)update2(L2[rt],l,mid,tl,tr);
    if(tr>mid)update2(R2[rt],mid+1,r,tl,tr);
    sum2[rt]=sum2[L2[rt]]+sum2[R2[rt]];
}
int main()
{
    ll n,m;
    ll ans,t;
    int q,op,a,b;
    while(~scanf("%lld%lld%d",&n,&m,&q))
    {
        init();
        while(q--)
        {
            scanf("%d%d%d",&op,&a,&b);
            if(op==1)update1(T1,1,n,a,b);
            else update2(T2,1,m,a,b);
            ans=n*m-((ll)sum1[1]*m+(ll)sum2[1]*n)+(ll)sum1[1]*(ll)sum2[1];
            if(sum1[1]==0)ans+=sum2[1];
            else if(sum2[1]==0)ans+=sum1[1];
            else ans++;
            printf("%lld
",ans);
        }
    }
}
原文地址:https://www.cnblogs.com/kkkek/p/11621399.html