洛谷P2894 [USACO08FEB]酒店Hotel 线段树+区间合并+多重tag


洛谷P2894 [USACO08FEB]酒店Hotel


标签

  1. 线段树
  2. 区间合并
  3. 多种懒惰标记

简明题意

  1. 维护一个01序列,支持三种操作
    1. 询问连续x长的区间的最小左端点。若不存在则输出0
    2. 将区间[L,R]全置为0
    3. 将区间[L,R]全置为0

思路

  • 主要说说tag的处理。由于对区间有两种操作,一个是置0,一个是置1,自然应该想到两种tag的处理方法。参见这里,较为详细地说明了如何处理多种标记。这篇博客里我再解释一次:

    1. 首先定义好你的tag
    2. 当你定义好多个tag后,首要的任务是分析通过多个tag得到当前节点的真实值的方法
    3. 由上面分析出的方法,再去定义tag间的运算(tag间的运算,指添加tag时的法则,也就是change时如何打一个tag而不影响别的tag)
    4. 如何传标记

    回到这里,

    1. 首先定义好tag,我们规定一个tag1表示当前节点应住房,tag2表示当前节点应退房
    2. 接下来应该分析通过多个tag得到当前节点的真实值的方法。当tag1=1,tag2=1,时如何处理当前节点的真实值?我们发现存在tag时新来的tag是可以直接覆盖的呢,因为永远是以最后一次操作为基准,所以直接覆盖。因此不可能同时存在两种标记,直接覆盖即可。
    3. 接下来考虑spread,由上面我们直接覆盖就好了
  • 其他的区间合并的方法比较简单,就不详细说了


注意事项

  • 一定要注意区间归并时的max的更新方法,它是由左右子树的max而来的,而不是左右子树的lmax或rmax来的!!!(好吧这是我自己不小心犯的错
  • 区间合并的题一定要弄清楚自己现在处理的是左子树还是右子树,别搞反了~ (这又是我自己犯的错)

总结


AC代码

#include<cstdio>
#include<algorithm>
using namespace std;

const int maxn = 50000 + 10;

struct Node
{
   int l, r;
   int lmax, rmax, max;
   int len;
   int tag1, tag2;
};

Node tree[maxn * 4];

void spread(int o)
{
   if (tree[o].tag1 == 1)
   {
       tree[o].lmax = tree[o].rmax = tree[o].max = 0;
       if (tree[o].l != tree[o].r)
       {
           tree[o * 2].tag2 = tree[o * 2 + 1].tag2 = 0;
           tree[o * 2].tag1 = tree[o * 2 + 1].tag1 = 1;
       }
       tree[o].tag1 = 0;
   }
   if (tree[o].tag2 == 1)
   {
       tree[o].lmax = tree[o].rmax = tree[o].max = tree[o].len;
       if (tree[o].l != tree[o].r)
       {
           tree[o * 2].tag2 = tree[o * 2 + 1].tag2 = 1;
           tree[o * 2].tag1 = tree[o * 2 + 1].tag1 = 0;
       }
       tree[o].tag2 = 0;
   }
}

void update(int o)
{
   if (tree[o].l != tree[o].r)
   {
       spread(o * 2), spread(o * 2 + 1);

       tree[o].lmax = tree[o * 2].lmax;
       if (tree[o * 2].lmax == tree[o * 2].len)
           tree[o].lmax += tree[o * 2 + 1].lmax;


       tree[o].rmax = tree[o * 2 + 1].rmax;
       if (tree[o * 2 + 1].rmax == tree[o * 2 + 1].len)
           tree[o].rmax += tree[o * 2].rmax;

       tree[o].max = max(tree[o * 2].max, tree[o * 2 + 1].max);
       tree[o].max = max(tree[o].max, tree[o * 2].rmax + tree[o * 2 + 1].lmax);
   }
}

void build(int o, int l, int r)
{
   tree[o].l = l, tree[o].r = r;
   tree[o].len = tree[o].lmax = tree[o].rmax = tree[o].max = r - l + 1;
   if (l == r)
       return;

   int mid = (l + r) / 2;
   build(o * 2, l, mid);
   build(o * 2 + 1, mid + 1, r);
}

int ask(int o, int x)
{
   spread(o);

   if (tree[o].max < x)
       return 0;
   
   if (tree[o].lmax >= x || tree[o].l == tree[o].r)
       return tree[o].l;
   
   if (tree[o * 2].max >= x)
       return ask(o * 2, x);
   else if (tree[o * 2].rmax + tree[o * 2 + 1].lmax >= x)
       return tree[o * 2].r - tree[o * 2].rmax + 1;
   else
       return ask(o * 2 + 1, x);
}

void change(int o, int l, int r, int type)
{
   spread(o);
   if (tree[o].l == l && tree[o].r == r)
   {
       if (type == 1)
           tree[o].tag2 = 0, tree[o].tag1 = 1;
       else
           tree[o].tag2 = 1, tree[o].tag1 = 0;
       spread(o);
       return;
   }

   int mid = (tree[o].l + tree[o].r) / 2;
   if (r <= mid)
       change(o * 2, l, r, type);
   else if (l > mid)
       change(o * 2 + 1, l, r, type);
   else
       change(o * 2, l, mid, type), change(o * 2 + 1, mid + 1, r, type);

   update(o);
}

int n, m;

void solve()
{
   scanf("%d%d", &n, &m);
   build(1, 1, n);

   while (m--)
   {
       int opt;
       scanf("%d", &opt);

       if (opt == 1)
       {
           int x;
           scanf("%d", &x);

           int pos;
           printf("%d\n", pos = ask(1, x));
           if (pos != 0)
               change(1, pos, pos + x - 1, 1);

       }
       else if (opt == 2)
       {
           int l, r;
           scanf("%d%d", &l, &r);

           change(1, l, l + r - 1, 2);
       }
   }
}

int main()
{
   //freopen("Testin.txt", "r", stdin);
   //freopen("Testout.txt", "w", stdout);
   solve();
   return 0;
}

双倍经验

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
以下是P4087 [USACO17DEC]Milk Measurement的c++代码: ```c++ #include<bits/stdc++.h> using namespace std; int n,d,i,x,minn=1e9,maxn=-1e9,sum=7;//注意sum要初始化为7,因为一开始有三个人挤奶! map<int,int> mp; struct node{ int day,milk,id;//day表示某一天,milk表示这一天的产奶量,id表示这头牛的编号 }a[100010]; bool cmp(node x,node y){ return x.day<y.day; } int main(){ scanf("%d%d",&n,&d); for(i=1;i<=n;i++){ scanf("%d%d%d",&a[i].day,&a[i].id,&a[i].milk); minn=min(minn,a[i].id);//记录最小的牛的编号 maxn=max(maxn,a[i].id);//记录最大的牛的编号 } sort(a+1,a+n+1,cmp);//排序 for(i=1;i<=n;i++){ int p=a[i].id; mp[p]+=a[i].milk;//记录每头牛产奶总量 if(mp[p]-a[i].milk>=mp[minn]&&mp[p]>=mp[minn]){//如果这头牛的产奶总量减去这一天的产奶量后等于最小产奶量且这头牛的产奶总量大于等于最小产奶量 sum--; } if(mp[p]>=mp[maxn]&&mp[p]-a[i].milk<mp[maxn]){//如果这头牛的产奶总量大于等于最大产奶量且这头牛的产奶总量减去这一天的产奶量小于最大产奶量 sum++; } if(mp[p]-a[i].milk<mp[maxn]&&mp[p]>=mp[maxn]){//如果这头牛的产奶总量减去这一天的产奶量小于最大产奶量且这头牛的产奶总量大于等于最大产奶量 if(mp[maxn]-mp[p]+a[i].milk>0)sum++; } mp[p]-=a[i].milk;//减去这一天的产奶量 if(i==n||a[i].day!=a[i+1].day){//如果到了新的一天或者到了最后一天 if(mp[maxn]!=mp[a[i].id]&&mp[a[i].id]>=mp[maxn])sum++;//如果这头牛的产奶总量不等于最大产奶量且这头牛的产奶总量大于等于最大产奶量 if(mp[maxn]==mp[a[i].id]){//如果这头牛的产奶总量等于最大产奶量 if(a[i].id==maxn)sum+=0;//如果这头牛就是最大产奶量的牛,那么不需要增加计数器 else sum++;//否则需要增加计数器 } if(mp[minn]!=mp[a[i].id]&&mp[a[i].id]>=mp[minn])sum++;//如果这头牛的产奶总量不等于最小产奶量且这头牛的产奶总量大于等于最小产奶量 if(mp[minn]==mp[a[i].id]){ if(a[i].id==minn)sum+=0;//如果这头牛就是最小产奶量的牛,那么不需要增加计数器 else sum++;//否则需要增加计数器 } } } printf("%d\n",sum); return 0; } ``` 该题的解题思路是模拟,需要注意细节问题。我们可以首先将输入的数据按天数排序,然后模拟每一天挤奶的情况,并根据题目要求进行计数即可。具体细节请见代码注释。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值