BZOJ2743 [HEOI2012]采花

58 篇文章 0 订阅

Address


Solution

  • 题目大意:多次询问区间 [l,r] [ l , r ] 有多少个次数 2 ≥ 2 的数。
  • 先考虑一种暴力,记 suf[i] s u f [ i ] 表示与位置 i i 颜色相同的下一个位置。
  • 对于询问 [l,r],对于每种颜色我们暴力找到满足 lsuf[i]<suf[suf[i]]r l ≤ s u f [ i ] < s u f [ s u f [ i ] ] ≤ r 的最小 i i ,则把这种颜色加入答案。
  • 我们会发现暴力多次重复找了相同的位置。
  • 因此对 m 个询问按 l l 从小到大排序,用一个树状数组维护区间和。
  • l有序,通过不断右移的指针 i(i<l) i ( i < l ) 来模拟暴力找的过程。
  • 考虑将位置 i i 移除对结果产生的影响:
    • 如果有 suf[i],则位置 suf[i] s u f [ i ] 之前已经被标记过了,因为出现次数要 2 ≥ 2 ,我们当前 i i 这个位置上的数已经不包括在询问的区间中了,所以 suf[i] 肯定不是出现次数 2 ≥ 2 的数的最后那一个,标记为0。
    • 如果有 suf[suf[i]] s u f [ s u f [ i ] ] ,则将位置 suf[suf[i]] s u f [ s u f [ i ] ] 标记为1。
    • 询问时即用树状数组求区间和,时间复杂度 O(nlogn) O ( n log ⁡ n )
    • Code

      #include <iostream>
      #include <cstdio>
      #include <cctype>
      #include <algorithm>
      #include <cstring>
      
      using namespace std;
      
      namespace inout
      {
          const int S = 1 << 20;
          char frd[S], *ihed = frd + S;
          const char *ital = ihed;
      
          inline char inChar()
          {
              if (ihed == ital)
                  fread(frd, 1, S, stdin), ihed = frd;
              return *ihed++;
          }
      
          inline int get()
          {
              char ch; int res = 0; bool flag = false;
              while (!isdigit(ch = inChar()) && ch != '-');
              (ch == '-' ? flag = true : res = ch ^ 48);
              while (isdigit(ch = inChar()))
                  res = res * 10 + ch - 48;
              return flag ? -res : res; 
          }
      
          char fwt[S], *ohed = fwt;
          const char *otal = ohed + S;
      
          inline char outChar(char ch)
          {
              if (ohed == otal)
                  fwrite(fwt, 1, S, stdout), ohed = fwt;
              return *ohed++ = ch;
          }
      
          inline void put(int x)
          {
              if (x > 9) put(x / 10);
              outChar(x % 10 + 48);
          }
      };
      using namespace inout;
      
      const int N = 2e6 + 5;
      int n, C, m;
      int suf[N], low[N], a[N], b[N], c[N], Ans[N];
      
      struct node
      {
          int l, r, z;
      
          bool operator < (const node &x) const 
          {
              return l < x.l;
          }
      }p[N];
      
      inline void Modify(int x, int y)
      {
          for (; x <= n; x += low[x]) b[x] += y;
      }
      
      inline int Query(int x)
      {
          int res = 0;
          for (; x; x ^= low[x]) res += b[x];
          return res;
      }
      
      int main()
      {
      //  freopen("flower.in", "r", stdin);
      //  freopen("flower.out", "w", stdout);
      
          n = get(); C = get(); m = get();
          for (int i = 1; i <= n; ++i) 
              a[i] = get(), low[i] = i & -i;
          for (int i = n; i >= 1; --i)
              suf[i] = c[a[i]], c[a[i]] = i;
      
          for (int i = 1; i <= m; ++i) 
              p[i].l = get(), p[i].r = get(), p[i].z = i;
          sort(p + 1, p + m + 1);
      
          for (int i = 1; i <= C; ++i) 
              if (c[i] && suf[c[i]]) Modify(suf[c[i]], 1); 
          int j = 1;
          for (int i = 1; i <= m; ++i)
          {
              for (; j < p[i].l; ++j)
              {
                  if (suf[j]) Modify(suf[j], -1);
                  if (suf[j] && suf[suf[j]]) Modify(suf[suf[j]], 1); 
              }
              Ans[p[i].z] = Query(p[i].r) - Query(p[i].l - 1);
          }
      
          for (int i = 1; i <= m; ++i)
              put(Ans[i]), outChar('\n');
          fwrite(fwt, 1, ohed - fwt, stdout);
      
      //  fclose(stdin); fclose(stdout);
          return 0;
      }
      
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值