ARC 068E Snuke Line

题意

\(m+1\)个站台,分别为0~m,每个小火车都从0出发,且会有一个常数\(d\)使得该小火车停在0,d,2d,3d,...等站台上。有\(n\)种纪念品,分别在\([l_i, r_i]\)处有卖,问\(d\)分别取1 ~ m时每个小火车最多能取多少种纪念品。

想法

对于一般的区间问题,总是想着固定某一断点去考虑另一端点...而恰恰忘了也可以取考虑区间的长度。
假设我们现在枚举的常数为\(d\),考虑一个错误的做法,把所有区间都覆盖上去,然后统计d,2d,3d,...这些点上一共有多少个纪念品。为什么错了呢?因为有的区间会覆盖多个\(d\)的倍数,算重然后使得答案变大。那怎么解决这个问题呢?考虑之所以会算重是因为区间长度过大从而能同时覆盖两个及以上的\(d\)的倍数,即区间长度\(len_i \geq d\),长度比\(d\)大的区间必然会被选中,那么先对区间长度拍个序,然后长度比\(d\)小的直接覆盖+查询即可。这里的区间覆盖+查询用了差分+树状数组,因为树状数组查询的正好就是前缀和。

Code

#include <cstdio>
#include <cmath>
#include <cstdlib>
#include <cstring>
#include <iostream>
#include <algorithm>
#define ll long long 
#define db double
#define N 300010
#define L(x) ((x)&(-x))
#define max(a, b) ((a) > (b) ? (a) : (b))
#define min(a, b) ((a) < (b) ? (a) : (b))
#define swap(T, a, b) ({T ttt = a; a = b; b = ttt;})
int n, m, L, R, C[N];
struct Seg
{
   int l, r, len;
}a[N];
bool cmp(const Seg a, const Seg b)
{
   return a.len < b.len;
}
void Add(int k, int x)
{
   for (; k <= m; k += L(k))
      C[k] += x;
}
int Sum(int k)
{
   int s = 0;
   for (; k; k -= L(k))
      s += C[k];
   return s;
}
int main()
{
   scanf("%d%d", &n, &m);
   memset(C, 0, sizeof(C)); 
   for (int i = 1; i <= n; i++)
   {
      scanf("%d%d", &L, &R);
      a[i] = (Seg) { L, R, R-L+1 };
   }
   std::sort(a+1, a+n+1, cmp);
   int k = 1; 
   for (int i = 1; i <= m; i++)
   {
      for (; k <= n && a[k].len < i; k++)
      {
	     Add(a[k].l, 1);
	     Add(a[k].r+1, -1);
      }
      int ans = n - k + 1; 
      for (int j = i; j <= m; j += i)
	     ans += Sum(j);
      printf("%d\n", ans);
   }
   return 0; 
}
posted @ 2017-01-29 11:24  zkGaia  阅读(354)  评论(0编辑  收藏  举报