2019河北省大学生程序设计竞赛(重现赛)I.Twinkle(CDQ分治)

题目链接

题目大意

二维平面上一开始有n个点,每个点有一个初始亮度s,每过一个时间单位,每个点的亮度会增加1,如果当前亮度为c,那么再增加一次之后亮度会变为0.
q个询问,每次询问给一个时刻t和一个矩阵,问该时刻的时候这个矩阵内点的总亮度是多少。
(n,q,x,y <= 5e4, c<=1e9,0<=s,t<=c)

题解:

把一个询问拆分成四个前缀和询问,则每次询问在(x,y)内的点,在t时刻的总亮度。因为超过c之后会重新计数,又因为(t<=c),所以在当前查询的时候,一个星星的亮度最多有一次超过c。超过部分的贡献是 (超出c的星星数量)*(c+1),所以要减去这个值。
所以查询变成,询问(0,0,x,y)内,初始亮度为[0,c-t]的星星数量和总的星星数量。转换成三维偏序问题,CDQ分治求一下就可以啦。
因为c太大所以要离散化。

#include<bits/stdc++.h>
#define ll long long
#define lowbit(x) (x&-x)
using namespace std;
const int maxn = 5e4 + 50;
ll sz[maxn], val[maxn];
void add(ll *a, int i, ll x){while(i < maxn) a[i] += x, i += lowbit(i);return;}
ll qry(ll *a, int i){ll res = 0;while(i) res += a[i], i -= lowbit(i); return res;}//树状数组操作
int n, q;
ll c, ans[maxn];
struct node{
    int x, y, op, id;//op代表操作类型,id代表一个询问的初始位置
    ll t, pos;//pos为离散化后的查询点/修改点
    bool operator < (const node& a)const {
        if(x!=a.x) return x < a.x;
        return op < a.op;//如果x坐标相同,插入要优先于查询
    }
}e[maxn*10], temp[maxn*10];
ll cc[maxn*4];//离散化数组
int num;//离散化用
void cdq(int l, int r)
{
    if(r-l <= 1) return;
    int mid = (r+l)>>1;
    cdq(l, mid); cdq(mid, r);
    int lp = l, rp = mid, o = 0;
    while(lp < mid && rp < r){
        if(e[lp].y <= e[rp].y){
            if(e[lp].op == -2){//加入星星
                add(sz, e[lp].pos, 1);
                add(val, e[lp].pos, e[lp].t);
            }
            temp[o++] = e[lp++];
        }
        else{
            if(e[rp].op != -2){
                ll v = qry(val, num);//总的初始价值
                ll cnt1 = qry(sz, num);//总的星星
                ll cnt2 = qry(sz, e[rp].pos);//在[0, c-t]的星星
                ans[e[rp].id] += e[rp].op*( v + cnt1*(e[rp].t) - (cnt1 - cnt2)*(c+1) );
            }
            temp[o++] = e[rp++];
        }
    }
    while(rp < r){//先把查询处理完
        if(e[rp].op != -2){
            ll v = qry(val, num);
            ll cnt1 = qry(sz, num), cnt2 = qry(sz, e[rp].pos);
            ans[e[rp].id] += e[rp].op*( v + cnt1*(e[rp].t) - (cnt1 - cnt2)*(c+1) );
        }
        temp[o++] = e[rp++];
    }
    for(int i = l; i < lp; ++i){//删除之前的更改
        if(e[i].op == -2){
            add(sz, e[i].pos, -1);
            add(val, e[i].pos, -e[i].t);
        }
    }
    while(lp < mid) temp[o++] = e[lp++];//再把左边剩余的处理完
    for(int i = 0; i < o; ++i) e[i+l] = temp[i];
    return;
}
int main()
{
	scanf("%d%d%lld", &n, &q, &c);
	num = 0;
	for(int i = 0; i < n; ++i){
        scanf("%d%d%lld", &e[i].x, &e[i].y, &e[i].t);
        e[i].op = -2; e[i].pos = e[i].t;
        cc[++num] = e[i].t;
	}
	int idq = n;
	for(int i = 0; i < q; ++i){
        int x1, x2, y1, y2; ll t;
        scanf("%lld%d%d%d%d", &t, &x1, &y1, &x2, &y2);
        e[idq].op = 1, e[idq].x = x1 - 1, e[idq].y = y1 - 1, e[idq].id = i, e[idq].t = t, e[idq++].pos = c-t;
        e[idq].op = 1, e[idq].x = x2, e[idq].y = y2, e[idq].id = i, e[idq].t = t, e[idq++].pos = c-t;
        e[idq].op = -1, e[idq].x = x2, e[idq].y = y1 - 1, e[idq].id = i, e[idq].t = t, e[idq++].pos = c-t;
        e[idq].op = -1, e[idq].x = x1 - 1, e[idq].y = y2, e[idq].id = i, e[idq].t = t, e[idq++].pos = c-t;
        cc[++num] = c - t;
	}
	sort(e, e+idq);
	sort(cc+1, cc+num+1); num = unique(cc+1,cc+1+num) - cc - 1;//按照x坐标从小到大排序
	for(int i = 0; i < idq; ++i) e[i].pos = lower_bound(cc+1,cc+1+num, e[i].pos) - cc;
	cdq(0, idq);
	for(int i = 0; i < q; ++i) printf("%lld\n", ans[i]);
}

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值