题解:P15369 『ICerOI Round 1』并非图论

P15369 『ICerOI Round 1』并非图论

简单题,秒掉了。

观察特殊性质 \(B\),容易得到最小的花费为 \(\sum_{i=l+1}^r i\),及所有的点向 \(l\) 连边。考虑哪些点是不必要连向 \(l\) 的。不难发现当 \(i\) 满足存在不是 \(l\) 的数 \(x\) 满足 \(i \text{ and } x = x\)\(i\) 是不必要的。及将 \(l+2^j\) 记为 \(x_i\),只要将 \(x_i\)\(l\) 连边,其他的都可以通过与 \(x_i\) 中的某一个连接达到最小的代价。此时我们也可以回到上面,发现有两种边。一种是 \(x_i\)\(l\) 的连边,代价为 \(x_i\);另一种是 \(y\)\(x_i\) 的连边,其中 \(y\in(l,r] \wedge y \notin x_i\),代价为 \(y\)

我们将上述做法拓展到一般情况。令 \(k_1\) 满足 \(2^{k_1} \le l < 2^{k_1+1}\)\(k_2\) 满足 \(2^{k_2} \le r < 2^{k_2+1}\),可以将询问 \([l,r]\) 分成三个区间,\([l,2^{k_1+1}) \bigcup [2^{k_1+1},2^{k_2}) \bigcup[2^{k_2},r]\)

对于区间 \([l,2^{k_1+1})\) 我们猜测大致做法一样,但是手模小数据发现最小代价会算少,我们将 \(l\) 的二进制位上某一位 \(i\)\(0\) 变成 \(1\) 并且将该位到最低位的 \(1\) 变成 \(0\) 时的数还是记为 \(x_i\),答案要加上 \(\sum_i(l-l\text{ and }x_i)\)

对于区间 \([2^{k_1+1},2^{k_2})\),我们将这个大区间拆分成若干个形如 \([2^k,2^{k+1})\) 的小区间,每个小区间内所有点连向小区间的左端点,并且容易从这个小区间内挑出一个数 \(x\) 满足前面的区间内存在数 \(y\) 满足\(y\subseteq x\) 并且将 \(x\)\(y\) 连边,不难发现这等价于这个小区间的左端点向 \(l\) 连边。

对于区间 \([2^{k_2},r]\),直接套用特殊性质 \(B\)

然后做完了,注意答案多取模。

#include <bits/stdc++.h>
using namespace std;
#define int long long
const int mod = 1e9 + 7;
const int INV = 500000004ll;
int id, n, Q;
int C(int x){
  int c = 0;
  while(x) ++c, x /= 2ll;
  return c - 1;
}
signed main(){
  ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
  cin >> id >> n >> Q;
  while(Q--){
    int l, r, j1 = 0, j2 = 0;
    cin >> l >> r;
    if(l == r){
      cout << 0 << ' ' << 0 << '\n'; 
      continue;
    }
    j1 = C(l), j2 = C(r);
    if(j1 == j2){ 
      int c1 = 0, c0 = 0;
      c1 = (l + 1 + r) % mod * ((r - l + mod) % mod) % mod * INV % mod;
      for(int j = 0; j < j1; j++){
        if(((l >> j) & 1) == 0){
          int x = l - (l & ((1ll << j) - 1)) + (1ll << j);
          if(x <= r){
            ++c0;
            c1 += l - (x & l); c1 %= mod;
          }
          else break;
        }
      }
      cout << c1 << ' ' << c0 << '\n';
      continue;
    }
    int ans1 = 0, ans2 = 0;
    if(l + 1 != (1ll << (j1 + 1))){
      int c1 = 0, c0 = 0;
      c1 = (l + 1 + ((1ll << (j1 + 1)) - 1)) % mod * ((((1ll << (j1 + 1)) - 1) - l + mod) % mod) % mod * INV % mod;
      for(int j = 0; j < j1; j++){
        if(((l >> j) & 1) == 0){
          int x = l - (l & ((1ll << j) - 1)) + (1ll << j);
          if(x <= ((1ll << (j1 + 1)) - 1)){
            ++c0;
            c1 += l - (x & l); c1 %= mod;
          }
          else break;
        }
      }
      ans1 += c1, ans1 %= mod;
      ans2 += c0, ans2 %= mod;
    }
    for(int j = j1 + 1; j < j2; j++){
      int x = (1ll << j), y = (1ll << (j + 1)) - 1;
      ans1 += (l + (x + 1 + y) % mod * ((y - x + mod) % mod) % mod * INV % mod) % mod + x % mod; ans1 %= mod;
      ans2++; ans2 %= mod;
    }
    int c0 = 0; int w = (r - (1ll << j2) + mod) % mod;
    for(int j = 0; j < j2; j++){
      if((1ll << j2) + (1ll << j) <= r) ++c0;
    }   
    ans1 += ((1ll << j2) + 1 + r) % mod * w % mod * INV % mod + l % mod + (1ll << j2) % mod; ans1 %= mod;
    ans2++; ans2 %= mod;     
    cout << ans1 << ' ' << ans2 << '\n';
  }
  return 0;
}
posted @ 2026-02-22 14:37  CJ-rnd  阅读(17)  评论(0)    收藏  举报