线段树——51nod1466 三角巧克力

题面:51nod1466
想了很久经过点拨之后才想到做法……(NOIP要爆零啦)
其实找到关键突破点就知道这题怎么做了。。。
这题的关键突破点就是保证起点在反对角线上
我们把行和列分开来,记录该行(列)目前还剩下多少巧克力
每次对一行进行操作时,我们去找到列上在这一列前面的离这一列最近的剩下的巧克力少于这一列的位置(对列进行操作同理)
听起来很拗口是不是?
就拿样例解释吧

比如我们要处理第2个操作,起始点是(6,1)
那么我们就在1~5列中找到离第6列最近的小于1的位置,就是3啦(3的剩余是0)
那么我们就把4~6列的巧克力取走了,答案就出来了
最后呢再把这一行的信息也修改一下(和列一样)
上述操作都可以用线段树来解决(包括找,就是一个二分)
离散化什么的就不再多说了(据说可以用动态开点的方法避免离散化%%%)
这题貌似使用set代码奇短666

#include <cstdio>
#include <algorithm>
#include <cstring>
#include <cmath>
#include <cstdlib>
#include <set>
#include <map>
#include <iostream>
#include <vector>
#include <queue>
#include <ctime>
#include <climits>
using namespace std;
inline int read(){
    int k=0,f=1;char ch=getchar();
    while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
    while(ch>='0'&&ch<='9'){k=k*10+ch-'0';ch=getchar();}
    return k*f;
}
inline void write(int x){
    if(x<0)putchar('-'),x=-x;
    if(x>9)write(x/10);putchar(x%10+'0');
}
inline void writeln(int x){
    write(x);puts("");
}
struct ppap{int x,y,f;}a[200010];
int b[2][200010],n,m,k=0;
struct tree{
    int t[800010];
    inline void build(int op,int l,int r,int nod){
        if(l==r){t[nod]=n-b[op^1][l]+2;return;}
        int mid=(l+r)>>1;
        build(op,l,mid,nod*2);build(op,mid+1,r,nod*2+1);
        t[nod]=min(t[nod*2],t[nod*2+1]);
    }
    inline void xg(int l,int r,int x,int v,int nod){
        if(l==r){t[nod]=min(t[nod],v);return;}
        int mid=(l+r)>>1;
        if(x<=mid)xg(l,mid,x,v,nod*2);
        else xg(mid+1,r,x,v,nod*2+1);
        t[nod]=min(t[nod*2],t[nod*2+1]);
    }
    inline void smin(int l,int r,int i,int v,int nod){
        if(l>i||t[nod]>v)return;
        if(l==r){k=l;return;}
        int mid=(l+r)>>1;
        smin(mid+1,r,i,v,nod*2+1);
        if(!k)smin(l,mid,i,v,nod*2);
    }//二分找位置
}t[2];
int main()
{
    n=read();m=read();
    for(int i=1;i<=m;i++){
        a[i].x=b[0][i]=read();a[i].y=b[1][i]=read();
        char c[5];scanf("%s",c);a[i].f=(c[0]=='U')?0:1;
    }
    sort(b[0]+1,b[0]+m+1);sort(b[1]+1,b[1]+m+1);
    int M[2];
    M[0]=unique(b[0]+1,b[0]+m+1)-b[0]-1;
    M[1]=unique(b[1]+1,b[1]+m+1)-b[1]-1;
    for(int i=1;i<=m;i++){
        a[i].x=lower_bound(b[0]+1,b[0]+M[0]+1,a[i].x)-b[0];
        a[i].y=lower_bound(b[1]+1,b[1]+M[1]+1,a[i].y)-b[1];
    }//以上离散化
    t[0].build(0,1,M[0],1);t[1].build(1,1,M[1],1);//分别维护行和列的两棵树
    for(int i=1;i<=m;i++)if(a[i].f==1){
        t[0].smin(1,M[0],a[i].x,a[i].y,1);
        writeln(b[0][a[i].x]-b[0][k]);
        t[1].xg(1,M[1],a[i].y,k,1);t[0].xg(1,M[0],a[i].x,a[i].y,1);//不要忘记自己这个点两个方向都要修改
        k=0;
    }else{
        t[1].smin(1,M[1],a[i].y,a[i].x,1);
        writeln(b[1][a[i].y]-b[1][k]);
        t[0].xg(1,M[0],a[i].x,k,1);t[1].xg(1,M[1],a[i].y,a[i].x,1);
        k=0;
    }
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值