这题.....队里都没怎么训练差分,导致败北...写了一堆线段树嘤嘤嘤,到最后也是超时,比赛结束后看到了差分的思想于是就去学了一手.
其实了解差分思想的一眼就能看出来是差分了.但是如果对n差分的话很明显会T呢,所以我们考虑从m组询问入手.
要不我先讲一下我目前了解的差分吧.
直白说的话,差分就是用一个差分数组mark保存val[i] - val[i - 1]的值,然后利用差分的性质就可以很方便的解决一类问题.
先说一个差分的性质.差分数组的前 i 项和就是原数组元素val[i],这个想一下想不出来的话可以自己手写差分数组,就知道为什么了.
那么很明显了呀,我们如果给差分数组中第i项的值加a,就相当于给原数组i及其之后的值都加了这个值,就可以很容易实现对一个区间加同一个值.
如何在区间末端截止呢,我们把闭区间后面的元素所对应的差分减a,后面的数就不会因为前面的数的改变而改变了.
so
下面说这个题很明显一个灯泡如果是亮着的,那么他一定是被按了奇数次,所以我们区间更新求和然后对每个数判断是否它上面的值是否是奇数即可.
复杂度O(Tn),妥妥的超时啦,我们发现是因为n过大才超时的,又因为询问只有1000次,所以我们考虑对m下手,稍加分析我们可以发现,如果将所有的询问
都读入,接着按照大小排序,依次访问所有值,如果发现此时出现左端点和右端点的次数之和为奇数时说明这个点之后的点一定是开着的,但是我们不知道开到哪,
所以我们只能让他开到下一个数的区间,再循环访问所有区间,我们就可以逐个统计出所有开着的灯了,这么一想是不是感觉巨简单.
参考代码:
1 #include <cstdio> 2 #include <cstring> 3 #include <algorithm> 4 using namespace std; 5 6 const int maxn = 1000 + 5; 7 struct node { 8 int id, k; 9 } q[maxn << 1]; 10 11 bool cmp(node a, node b) { 12 return a.id < b.id; 13 } 14 15 int main() { 16 int t, n, m, l, r, tot, ans, sum, _case = 0; 17 scanf("%d", &t); 18 while(t --) { 19 tot = sum = ans = 0; 20 scanf("%d %d", &n, &m); 21 while(m --) { 22 scanf("%d %d", &l, &r); 23 q[++ tot].id = l; q[tot].k = 1; 24 q[++ tot].id = r + 1; q[tot].k = -1; 25 } 26 sort(q + 1, q + tot + 1, cmp); 27 for(int i = 1; i <= tot - 1; i ++) { 28 sum += q[i].k; 29 if(sum & 1) ans += q[i + 1].id - q[i].id; 30 } 31 printf("Case #%d: %d ", ++ _case, ans); 32 } 33 return 0; 34 }
放上我多此一举离散化实现的代码:
1 #include <cstdio> 2 #include <cstring> 3 #include <algorithm> 4 using namespace std; 5 6 const int maxn = 1000 + 5; 7 int mark[maxn << 1]; 8 struct node { 9 int l, r; 10 } q[maxn]; 11 12 int ls[maxn << 1]; 13 14 int main() { 15 int t, n, m, tot, _case = 0; 16 scanf("%d", &t); 17 while(t --) { 18 memset(mark, 0 ,sizeof mark); 19 tot = 0; 20 scanf("%d %d", &n, &m); 21 for(int i = 1; i <= m; i ++) { 22 scanf("%d %d", &q[i].l, &q[i].r); 23 q[i].r ++; 24 ls[++ tot] = q[i].l; 25 ls[++ tot] = q[i].r; 26 } 27 sort(ls + 1, ls + 1 + tot); 28 int num = unique(ls + 1, ls + 1 + tot) - ls - 1; 29 for(int i = 1; i <= m; i ++) { 30 q[i].l = lower_bound(ls + 1, ls + 1 + num, q[i].l) - ls; 31 q[i].r = lower_bound(ls + 1, ls + 1 + num, q[i].r) - ls; 32 mark[q[i].l] ++; 33 mark[q[i].r] --; 34 } 35 int ans = 0, sum = 0; 36 for(int i = 1; i <= num; i ++) { 37 sum += mark[i]; 38 if(sum & 1) ans += ls[i + 1] - ls[i]; 39 } 40 printf("Case #%d: %d ", ++_case, ans); 41 } 42 return 0; 43 }