比赛-ZR DAY1 (04 Aug, 2018)

网址:http://www.zhengruioi.com/contest/58

A. 数对子

题目可以转化为,在一些区间的并集中,计算二进制中 1 的数量为奇数的数的个数 s0,以及 1 的数量为偶数的数的个数 s1,答案就是 s0*s1 。因为异或之后 1 的数量为奇数,说明异或前两数奇偶性相异。考虑处理 [0, i] 区间中,1 的数量为奇数的数的个数,写一个 cal1(i) 来计算。

分析发现 (0, 1), (2, 3), (4, 5) ... 这些数对,二进制下的 1 的个数总是相差 1 ,因为个位相差 1. 故 cal1(5) = 3 ,因为 (0, 1), (2, 3), (4, 5) 这三对数里,每一对都可有一个 1 的个数是奇数的数。推理可得:cal1(奇数 n) = (n + 1) / 2 ,cal1(偶数 n) = n / 2 + (pop_count(n) & 1) 。这之后维护一个线段树处理并集就行了。

 

 1 #include <cstdio>
 2 #include <algorithm>
 3 
 4 using namespace std;
 5 
 6 typedef long long ll;
 7 
 8 const ll MX = (1ll<<32)-1;
 9 const ll _N = 5000000;
10 
11 int STCnt = 1, rt = 1, L[_N], R[_N], Lazy[_N];
12 ll S1[_N], S0[_N];
13 
14 ll cal1(ll n)
15 {
16     if (n&1) return (n+1)/2;
17     return n/2+(__builtin_popcount(n)&1);
18 }
19 
20 void MDF(int &p, ll l, ll r, ll s, ll t)
21 {
22     if (!p) p = ++STCnt;
23     if (s <= l && r <= t) { S1[p] = cal1(r)-cal1(l-1), S0[p] = r-l+1-S1[p], Lazy[p] = 1; return; }
24     if (Lazy[p]) return;
25     if (!L[p]) L[p] = ++STCnt;
26     if (!R[p]) R[p] = ++STCnt;
27     ll mid = (l+r)>>1;
28     if (s <= mid && t >= l) MDF(L[p], l, mid, s, t);
29     if (s <= r && t > mid) MDF(R[p], mid+1, r, s, t);
30     S0[p] = S0[L[p]]+S0[R[p]];
31     S1[p] = S1[L[p]]+S1[R[p]];
32     return;
33 }
34 
35 int main()
36 {
37     int N;
38     scanf("%d", &N);
39     while (N--) {
40         ll l, r;
41         scanf("%lld%lld", &l, &r);
42         MDF(rt, 1, MX, l, r);
43         printf("%lld\n", S1[rt]*S0[rt]);
44     }
45     return 0;
46 }

 

B. 逆序对

好难啊 Orz 。显然……奇数集合插入后满足单调性,并且各元素不会贡献逆序对。也就是说,对每个奇数,在偶数序列中找一个使新增逆序对最少的位置插入,把每次的新增逆序对个数加起来就是答案,而不用考虑之前已添加过的奇数元素。

 

D. 盖房子

据说是三次容斥原理,分别对应“K个方格”“正负对角线”“所有禁区”这三个容斥对象。考试时想到了前两个容斥写了发 DFS+容斥 ,拿到 10 分。还不如状压DP+记忆化搜索。之后老师讲得略快(其实是因为我太弱了),并不能理解。代码也不会写 QwQ。

posted @ 2018-08-04 17:34  derchg  阅读(176)  评论(0编辑  收藏  举报