hdu5481

一开始尝试用线段树,结果超时。

超时的代码:

#include<stdio.h>
#include<string.h>
#include<iostream>
#include<algorithm>
using namespace std;
#define N 200010
#define MOD 1000000007

struct node{
    int l,r;
    int cou;
};

int num[N];
int l[N],r[N];
int m;
struct node tree[4*N];
int alll,allr;

void lisanhua(int n){
    sort(num,num+n);
    m=unique(num,num+n)-num;

    return;
}

void create(int root,int l,int r){
    if(l+1==r){
        tree[root].l=l;
        tree[root].r=r;
        tree[root].cou=0;

        return;
    }
    else{
        int middle=(l+r)>>1;

        create(root<<1,l,middle);
        create((root<<1)+1,middle,r);
        tree[root].l=l;
        tree[root].r=r;
        tree[root].cou=0;

        return;
    }
}

void push_down(int root){
    if(tree[root].cou){
        tree[root<<1].cou+=tree[root].cou;
        tree[(root<<1)+1].cou+=tree[root].cou;
        tree[root].cou=0;
    }

    return;
}

int query(int root,int l,int r){
    if(l+1==r){
        return tree[root].cou;
    }
    else{
        int middle=(r+l)>>1;

        push_down(root);
        if(allr<=middle){
            return query(root<<1,l,middle);
        }
        else{
            return query((root<<1)+1,middle,r);
        }
    }
}

void add(int root,int l,int r){
    if(alll<=l&&allr>=r){//这里想错了,所以写成了 l<=alll&&r>=allr
        tree[root].cou+=1;

        return;
    }
    else{
        int middle=(l+r)>>1;

        if(alll<middle){
            add(root<<1,l,middle);
        }
        if(allr>middle){
            add((root<<1)+1,middle,r);
        }

        return;
    }
}

int hush(int n){
    return lower_bound(num,num+m,n)-num+1;
}

long long int km(long long int x,long long int y){
    long long int ans=1;

    while(y){
        if(y%2){
            y=y/2;
            ans=ans*x%MOD;
        }
        y=y/2;
        x=x*x%MOD;
    }

    return ans;
}

int main(){
    int t;
    int n;
    int numcou;
    long long int ans;

    scanf("%d",&t);
    while(t--){
        scanf("%d",&n);

        numcou=0;
        for(int i=0;i<n;i++){
            scanf("%d%d",&l[i],&r[i]);
            num[numcou++]=l[i];
            num[numcou++]=r[i];
        }
        lisanhua(2*n);

        create(1,1,m);

        ans=0;
        //printf("%d\n",m);
        for(int i=0;i<n;i++){
            int templ=hush(l[i]);
            int tempr=hush(r[i]);

            for(int j=templ+1;j<=tempr;j++){
                alll=j-1;
                allr=j;

                int tempcou=query(1,1,m);

                //printf("%d %d %d %d %d\n",tempcou,num[j-1],num[j-2],j-1,j);
                ans=(ans+km(2,i-tempcou)*km(2,n-i-1)%MOD*(num[j-1]-num[j-2])%MOD)%MOD;
            }
            alll=templ;
            allr=tempr;
            //printf("%d %d %d %d\n",l[i],r[i],alll,allr);
            add(1,1,m);
        }

        printf("%lld\n",ans);
    }
}

部分优化后,还是超时。

超时的代码:

#include<stdio.h>
#include<string.h>
#include<iostream>
#include<algorithm>
using namespace std;
#define N 200010
#define MOD 1000000007

int cou[N*4];
int num[N];
int l[N],r[N];
int m;
int alll,allr;
long long int mi[N];

void lisanhua(int n){
    sort(num,num+n);
    m=unique(num,num+n)-num;

    return;
}

void create(int root,int l,int r){
    if(l+1==r){
        cou[root]=0;

        return;
    }
    else{
        int middle=(l+r)>>1;

        create(root<<1,l,middle);
        create((root<<1)+1,middle,r);
        cou[root]=0;

        return;
    }
}

void push_down(int root){
    if(cou[root]){
        cou[root<<1]+=cou[root];
        cou[(root<<1)+1]+=cou[root];
        cou[root]=0;
    }

    return;
}

int query(int root,int l,int r){
    if(l+1==r){
        return cou[root];
    }
    else{
        int middle=(r+l)>>1;

        push_down(root);
        if(allr<=middle){
            return query(root<<1,l,middle);
        }
        else{
            return query((root<<1)+1,middle,r);
        }
    }
}

void add(int root,int l,int r){
    if(alll<=l&&allr>=r){
        cou[root]+=1;

        return;
    }
    else{
        int middle=(l+r)>>1;

        if(alll<middle){
            add(root<<1,l,middle);
        }
        if(allr>middle){
            add((root<<1)+1,middle,r);
        }

        return;
    }
}

int hush(int n){
    return lower_bound(num,num+m,n)-num+1;
}

int main(){
    int t;
    int n;
    int numcou;
    long long int ans;

    mi[0]=1;
    for(int i=1;i<N;i++){
        mi[i]=mi[i-1]*2%MOD;
    }

    scanf("%d",&t);
    while(t--){
        scanf("%d",&n);

        numcou=0;
        for(int i=0;i<n;i++){
            scanf("%d%d",&l[i],&r[i]);
            num[numcou++]=l[i];
            num[numcou++]=r[i];
        }
        lisanhua(2*n);

        create(1,1,m);

        ans=0;
        //printf("%d\n",m);
        for(int i=0;i<n;i++){
            int templ=hush(l[i]);
            int tempr=hush(r[i]);

            for(int j=templ+1;j<=tempr;j++){
                alll=j-1;
                allr=j;

                int tempcou=query(1,1,m);

                //printf("%d %d %d %d %d\n",tempcou,num[j-1],num[j-2],j-1,j);
                ans=(ans+mi[i-tempcou]*mi[n-i-1]%MOD*(num[j-1]-num[j-2])%MOD)%MOD;
            }
            alll=templ;
            allr=tempr;
            //printf("%d %d %d %d\n",l[i],r[i],alll,allr);
            add(1,1,m);
        }

        printf("%lld\n",ans);
    }
}

想到可能是每一个区间都遍历一遍,会很超时,于是将计算答案留到最后,一起计算,最后整个从头到尾遍历一遍所有区间的并集就可以了。

所以整体的思路是:

每个单位段,比如说(1,2),(100,101),假设有这个单元段的区间是x个,总共是n个,那么这个单元段的贡献是 (2^x-1)*2^(n-x)。因为 l 和 r 的范围比较大,所以可以离散化后再做。

ac的代码:

#include<stdio.h>
#include<string.h>
#include<iostream>
#include<algorithm>
using namespace std;
#define N 200010
#define MOD 1000000007

int cou[N*4];
int num[N];
int l[N],r[N];
int m;
int n;
int alll,allr;
long long int mi[N];
long long int ans;

void lisanhua(int n){
    sort(num,num+n);
    m=unique(num,num+n)-num;

    return;
}

void create(int root,int l,int r){
    if(l+1==r){
        cou[root]=0;

        return;
    }
    else{
        int middle=(l+r)>>1;

        create(root<<1,l,middle);
        create((root<<1)+1,middle,r);
        cou[root]=0;

        return;
    }
}

void push_down(int root){
    if(cou[root]){
        cou[root<<1]+=cou[root];
        cou[(root<<1)+1]+=cou[root];
        cou[root]=0;
    }

    return;
}

int query(int root,int l,int r){
    if(l+1==r){
        return cou[root];
    }
    else{
        int middle=(r+l)>>1;

        push_down(root);
        if(allr<=middle){
            return query(root<<1,l,middle);
        }
        else{
            return query((root<<1)+1,middle,r);
        }
    }
}

void add(int root,int l,int r){
    if(alll<=l&&allr>=r){
        cou[root]+=1;

        return;
    }
    else{
        int middle=(l+r)>>1;

        if(alll<middle){
            add(root<<1,l,middle);
        }
        if(allr>middle){
            add((root<<1)+1,middle,r);
        }

        return;
    }
}

int hush(int n){
    return lower_bound(num,num+m,n)-num+1;
}

void nodepush_down(int root,int l,int r){
    if(l+1==r){
        int temp=cou[root];

        ans=(ans+((mi[temp]-1)%MOD+MOD)%MOD*mi[n-temp]%MOD*(num[r-1]-num[l-1])%MOD)%MOD;

        return;
    }
    else{
        int middle=(l+r)>>1;

        push_down(root);
        nodepush_down(root<<1,l,middle);
        nodepush_down((root<<1)+1,middle,r);

        return;
    }
}

int main(){
    int t;
    int numcou;

    mi[0]=1;
    for(int i=1;i<N;i++){
        mi[i]=mi[i-1]*2%MOD;
    }

    scanf("%d",&t);
    while(t--){
        scanf("%d",&n);

        numcou=0;
        for(int i=0;i<n;i++){
            scanf("%d%d",&l[i],&r[i]);
            num[numcou++]=l[i];
            num[numcou++]=r[i];
        }
        lisanhua(2*n);

        create(1,1,m);

        //printf("%d\n",m);
        for(int i=0;i<n;i++){
            alll=hush(l[i]);
            allr=hush(r[i]);
            add(1,1,m);
        }

        ans=0;
        nodepush_down(1,1,m);

        printf("%lld\n",ans);
    }
}


参考链接:

http://blog.csdn.net/u014679804/article/details/48770245
看别人的思路更简单,是在区间的开始+1,在区间的结束-1,把所有的区间加进去后,最后求答案,是再遍历。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值