zstuoj 4186 表白计划(区间操作x线段树)

本文详细解析了ZSTU2015校赛中的一道数据结构竞赛题,包括题目的背景、思路和具体实现。通过模拟操作、合并、查找、去重等基本技巧,利用线段树优化区间查询效率,最终实现高效解决问题的方法。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题意:
。。
这道是zstu2015校赛题。。
传送
思路:
对于第一个询问,先维护差分序列,然后求对差分序列求两次前缀和得到前缀和。
对第二个询问。
假设用pair<int, int>来表示区间。
先把表白计划按girl分开存,对每个girl处理出她不发好人卡的区间。
这一步是 O(mlogm),m 为区间数
建一个线段树,节点 v(l,r)包含girl_l到girl_r的不发好人卡的区间,注意要去掉那些被其他区间完全包含的区间。
处理出来的区间全部都从小到大排序。
每一层所有节点包含的区间数 O(m)
所以这一步时间复杂度 O(mlogmlogn),空间 O(mlogn)
最后处理查询,可以用二分地在一个有序的区间序列里查找是否包含某个区间。而找id最小的就很容易了。。
不错的一道数据结构题,考察了基本的对区间的模拟操作,合并,查找,去重等。也对线段树有很好的利用。

const int N = 80005;

typedef pair<int, int> pii;
typedef long long LL;

LL prf[N];
vector<pii> gl[N];
int n, m, t;

void range_inv(vector<pii>& v, int t) {
    vector<pii> tmp;
    int cur = 1;
    for(int i = 0; i < v.size(); ++ i) {
        if ( cur < v[i].first ) {
            tmp.push_back( make_pair(cur, v[i].first - 1) );
        }
        cur = max ( cur, v[i].second + 1 );
    }
    if ( cur <= t ) tmp.push_back( make_pair(cur, t) );
    tmp.swap(v);
}

bool contain(const vector<pii>& v, int l, int r) {
    int pos = upper_bound( v.begin(), v.end(), make_pair(l, INT_MAX) ) - v.begin();
    if ( !pos || v[pos - 1].second < r ) return 0;
    return 1;
}

vector<pii> tr[N<<2];
int qL, qR;
#define lc o<<1
#define rc o<<1|1
void build(int o, int l, int r) {
    vector<pii>& v = tr[o];
    if ( l == r ) {
        v = gl[l];
    } else {
        int m = (l + r) >> 1;
        build(lc, l, m);
        build(rc, m + 1, r);
        vector<pii>().swap(v);
        rep(i, l, r) v.insert(v.end(), gl[i].begin(), gl[i].end());
        sort(v.begin(), v.end());
        int top = 1;
        for (int i = 1; i < v.size(); ++ i)
            if ( v[i].second > v[top - 1].second ) {
                if ( v[i].first == v[top - 1].first ) {
                    v[top - 1].second = v[i].second;
                } else {
                    v[top ++] = v[i];
                }
            }
        v.resize(top);
    }
}

int query(int o, int l, int r) {
    if ( l == r ) return l;
    int m = (l + r) >> 1;
    if ( contain(tr[lc], qL, qR) )
        return query(lc, l, m);
    return query(rc, m + 1, r);
}

int main() {
#ifdef _LOCA_ENV_
    freopen("input.in", "r", stdin);
#endif // _LOCA_ENV
    while ( scanf("%d%d%d", &n, &m, &t) != EOF ) {
        memset(prf, 0, sizeof(prf));
        rep(i, 1, n) vector<pii>().swap(gl[i]);
        rep(i, 1, m) {
            int l, r, x;
            scanf("%d%d%d", &l, &r, &x);
            prf[l] += 1;
            prf[r+1] -= 1;
            gl[x].push_back( make_pair(l, r) );
        }
        rep(i, 1, t) prf[i] += prf[i-1];
        rep(i, 1, t) prf[i] += prf[i-1];
        rep(i, 1, n) {
            sort(gl[i].begin(), gl[i].end());
            range_inv(gl[i], t);
        }

        build(1, 1, n);

        int z = 0, q;
        scanf("%d", &q);
        rep(o, 1, q) {
            int l, r;
            scanf("%d%d", &l, &r);
            l += z, r += z;
            if ( r > t ) {
                l = t - ( r - l );
                r = t;
            }
            qL = l, qR = r;
            if ( !contain(tr[1], l, r) )
                z = 0;
            else
                z = query(1, 1, n);
            printf("%lld %d\n", prf[r] - prf[l-1], z);
        }
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值