Monkeying Around Gym - 101350F 线段树区间染色+扫描线

本文介绍了一种使用线段树解决区间更新问题的方法,通过将问题转化为区间染色,并采用类似扫描线的策略来高效地统计最终状态。适用于竞赛编程中涉及大量区间操作的题目。

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

http://codeforces.com/gym/101350/problem/F
题意:
n只猴子坐在椅子上,有m次区间更新(笑话), 没听过这个笑话的猴子会掉到地上,原来听过这个笑话的猴子会做到椅子上。
问你最后有几只猴子坐在椅子上
数据输入就是线段树的经典输入。 由此联想到用线段树。
其次 猴子最后的状态其实只与他最后听到的那个笑话有关。
因此 我们可以把这个转化为区间染色问题既记录每只猴子最后听的笑话的颜色是啥。
然后,用类似于扫描线的方式。记录每个笑话的影响范围,左端点赋值为1 右端点赋值为-1. 排序之后一个一个点的扫描即可。

#include<iostream>
#include<cstdio>
#include<map>
#include<algorithm>
#include<functional>
#include<cstring>
using namespace std;
#define lson l,m,rt<<1
#define rson m+1,r,rt<<1|1
const int MAX=1e5+10;
int add[MAX<<2];
void build(int l,int r,int rt)
{
    add[rt]=-1;
    if(l==r) return;
    int m=(l+r)>>1;
    build(lson);
    build(rson);
}
void pushdown(int rt)
{
    if(add[rt]!=-1)
    {
        add[rt<<1]=add[rt<<1|1]=add[rt];
        add[rt]=-1;
    }
}
void update(int L,int R,int c,int l,int r,int rt)
{
    if(L<=l && R>=r)
    {
        add[rt]=c;
        //printf("[%d,%d]\n",l,r);
        return;
    }
    pushdown(rt);
    int m=(l+r)>>1;
    if(L<=m) update(L,R,c,lson);
    if(R>m) update(L,R,c,rson);
}
int query(int p,int l,int r,int rt)
{
    if(add[rt]!=-1)
        return add[rt];
    if(l==r)
        return -1;
    int m=(l+r)>>1;
    if(p<=m) return query(p,lson);
    else if(p>m) return query(p,rson);
}
class node
{
public:
    int clor,h,v;
    bool operator < (const node & b) const
    {
        return h<b.h;
    }
};
int tot=0;
node nodes[MAX<<1];
void adds(int h,int id,int v)
{
    nodes[tot].clor=id;
    nodes[tot].h=h;
    nodes[tot].v=v;
    tot++;
}
int cloer[MAX];
int main()
{
    int T;
    scanf("%d",&T);
    while(T--)
    {
        memset(cloer,0,sizeof cloer);
        int n,m;
        tot=0;
        scanf("%d %d",&n,&m);
        build(1,n,1);
        int x,l,k;
        for(int i=0;i<m;i++)
        {
            scanf("%d %d %d",&x,&l,&k);
            int hi=min(x+k+1,n+1);
            int low=max(x-k,1);
            adds(low,l,1);
            adds(hi,l,-1);
            update(low,hi-1,l,1,n,1);
        }
        sort(nodes,nodes+tot);
        int perh=1;
        int index=0;
        int ans=0;
        //cout<<"tot"<<tot<<endl;
        while(index<tot)
        {
            //cout<<index<<endl;
            while(perh==nodes[index].h && index<tot)
            {
                //printf("nodes[%d]:%d %d %d\n",index,nodes[index].clor,nodes[index].h,nodes[index].v);
                node &cnt=nodes[index];
                cloer[cnt.clor]+=cnt.v;
                index++;
            }
            for(int i=perh;i<nodes[index].h;i++)
            {
                int p=query(i,1,n,1);
                //printf("query(%d,%d,%d,%d)\n",i,1,n,1);
                if(p==-1) continue;
                //printf("p:%d\n",p);
                if(cloer[p]==1)
                    ans++;
            }
            perh=nodes[index].h;
        }
        cout<<n-ans<<endl;
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值