HDU -- 5700 区间交 【思维 + 线段树】

传送门
//中文题面就不说题意了.
//思路: 暴力枚举左端点, 在这个左端点上的右端点++, 然后对于每一个左端点找到刚好出现了k次的右端点, 并且这个右端点应该尽可能的靠右, 这样包含的数才尽可能的多. 还要满足位置关系. 那么找刚好出现了k次的右端点, 就用线段树维护, 二分的去找这个点. 最后求一个前缀更新答案就可以了.

AC Code

/** @Cain */
const int maxn = 1e5+5;
int cas=1;
int n,k,m;
ll t[maxn*4],sum[maxn],a[maxn];
vector<int> E[maxn];
void pushup(int rt)
{
    t[rt] = t[rt<<1] + t[rt<<1|1];
}
void build(int rt,int l,int r)
{
    t[rt]=0;
    if(l==r) return;
    int mid=(l+r)>>1;
    build(rt*2,l,mid);
    build(rt*2+1,mid+1,r);
}

void update(int l,int r,int rt,int pos)
{
    if(l==r){
        t[rt]++;
        return;
    }
    int mid=(l+r)>>1;
    if(pos<=mid)update(l,mid,rt<<1,pos);
    else update(mid+1,r,rt<<1|1,pos);
    pushup(rt);
}

int query(int l,int r,int rt,int p)
{
    if(t[rt]return -1;
    if(l==r) return l;
    int mid = (l+r)>>1;
    if(p <= t[rt<<1|1]) return query(mid+1,r,rt<<1|1,p); //尽量的找右端点.尽量的往右找.
    else return query(l,mid,rt<<1,p-t[rt<<1|1]);
}
void solve()
{
    while(~scanf("%d%d%d",&n,&k,&m)){
        build(1,1,n);
        for(int i=1;i<=n;i++){
            scanf("%lld",&a[i]);
            sum[i]=sum[i-1]+a[i];   //前缀
            E[i].clear();
        }
        for(int i=1;i<=m;i++){
            int l,r;
            scanf("%d%d",&l,&r);
            E[l].push_back(r);
        }
        ll ans = 0;
        for(int i=1;i<=n;i++){
            for(int j=0; j1,n,1,E[i][j]);
            int p = query(1,n,1,k);   //p是保证是出现了刚好k次右端点值,并且尽可能的往右走.
            if(p >= i ) ans = max(ans,sum[p]-sum[i-1]);
            //如果还是小于此时的左端点,那么这样刚好出现了k次的区间就不存在.
        }
        printf("%lld\n",ans);
    }
}

你可能感兴趣的:(线段树/RMQ/扫描线,想法思维题)