The Preliminary Contest for ICPC Asia Shanghai 2019 B. Light bulbs(卡了线段树空间的思维题)

传送门:https://nanti.jisuanke.com/t/41399

题目描述

There are N N N light bulbs indexed from 0 0 0 to N − 1 N−1 N1. Initially, all of them are off.

A F L I P FLIP FLIP operation switches the state of a contiguous subset of bulbs. F L I P ( L , R ) FLIP(L, R) FLIP(L,R) means to flip all bulbs xx such that L ≤ x ≤ R L \leq x \leq R LxR. So for example, F L I P ( 3 , 5 ) FLIP(3, 5) FLIP(3,5) means to flip bulbs 3 , 4 and 5, and F L I P ( 5 , 5 ) FLIP(5,5) FLIP(5,5) means to flip bulb 5.

Given the value of N N N and a sequence of M M M flips, count the number of light bulbs that will be on at the end state.

输入格式

The first line of the input gives the number of test cases, T. T test cases follow. Each test case starts with a line containing two integers N and M, the number of light bulbs and the number of operations, respectively. Then, there are M more lines, the ii-th of which contains the two integers L i L_i Liand R i R_i Ri, indicating that the ii-th operation would like to flip all the bulbs from L i L_i Li
to R i R_i Ri, inclusive.

1 ≤ T ≤ 1000 1 \leq T \leq 1000 1T1000

1 ≤ N ≤ 1 0 6 1 \leq N \leq 10^6 1N106

1 ≤ M ≤ 1000 1 \leq M \leq 1000 1M1000

0 ≤ L i ≤ R i ≤ N − 1 0 \leq L_i \leq R_i \leq N-1 0LiRiN1

输出格式

For each test case, output one line containing Case # x : y x: y x:y, where x is the test case number (starting from 1) and y is the number of light bulbs that will be on at the end state, as described above.

样例输入

2
10 2
2 6
4 8
6 3
1 1
2 3
3 4

样例输出

Case #1: 4
Case #2: 3



① 线段树做法(MLE)

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
 
const int maxn = 1e6 + 10;
int t, n, m, x, y;
struct node { int l, r, sum, flip; } tree[maxn << 2];
 
inline const int read()
{
    int x = 0, f = 1; char ch = getchar();
    while (ch < '0' || ch > '9') { if (ch == '-') f = -1; ch = getchar(); }
    while (ch >= '0' && ch <= '9') { x = (x << 3) + (x << 1) + ch - '0'; ch = getchar(); }
    return x * f;
}
 
inline int ls(int id) { return id << 1; }
inline int rs(int id) { return id << 1 | 1; }
 
void push_up(int id)
{
    tree[id].sum = tree[ls(id)].sum + tree[rs(id)].sum;
}
 
void push_down(int id)
{
    if (tree[id].flip)
    {
        tree[ls(id)].flip ^= 1; tree[rs(id)].flip ^= 1;
        tree[ls(id)].sum = tree[ls(id)].r - tree[ls(id)].l + 1 - tree[ls(id)].sum;
        tree[rs(id)].sum = tree[rs(id)].r - tree[rs(id)].l + 1 - tree[rs(id)].sum;
        tree[id].flip = 0;
    }
}
 
void build(int id, int l, int r)
{
    tree[id].l = l; tree[id].r = r;
    tree[id].sum = tree[id].flip = 0;
    if (l == r) return;
    int mid = (l + r) >> 1;
    build(ls(id), l, mid);
    build(rs(id), mid + 1, r);
    push_up(id);
}
 
void update(int id, int l, int r)
{
    if (tree[id].l == l && tree[id].r == r)
    {
        tree[id].flip ^= 1;
        tree[id].sum = r - l + 1 - tree[id].sum;
        return;
    }
    push_down(id);
    int mid = (tree[id].l + tree[id].r) >> 1;
    if (r <= mid) update(ls(id), l, r);
    else if (l > mid) update(rs(id), l, r);
    else
    {
        update(ls(id), l, mid);
        update(rs(id), mid + 1, r);
    }
    push_up(id);
}
 
int query(int id, int l, int r)
{
    if (tree[id].l == l && tree[id].r == r) return tree[id].sum;
    push_down(id);
    int mid = (tree[id].l + tree[id].r) >> 1;
    if (r <= mid) return query(ls(id), l, r);
    if (l > mid) return query(rs(id), l, r);
    return query(ls(id), l, mid) + query(rs(id), mid + 1, r);
}
 
int main()
{
    t = read();
    for (int i = 1; i <= t; i++)
    {
        printf("Case #%d: ", i);
        n = read(); m = read();
        build(1, 1, n);
        while (m--)
        {
            x = read() + 1; y = read() + 1;
            update(1, x, y);
        }
        printf("%d\n", query(1, 1, n));
    }
    return 0;
}

对于每一对Li、Ri,我们将其定义为:将Li之前的所有灯全都filp一次,再将Ri之前的所有灯全都filp一次,如图,即为将C段先翻转一次,再将A段翻转一次,这样一来,C段的灯被翻转了2次,即没有变化,而B段则被翻转了1次,达成了翻转[Li, Ri]区间的效果。
在这里插入图片描述
故,我们将所有的 Li(1 ~ Li-1)和 Ri+1(要包括Ri,故为Ri+1之前)保存在一个数组 v v v中,再将其由小到大排序遍历,维护在此坐标之前亮着的灯的数量 a n s ans ans(翻转一次即为 a n s = v [ i ] − a n s ans = v[i] - ans ans=v[i]ans

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
 
const int maxn = 1e6 + 10;
int v[maxn];
 
inline const int read()
{
    int x = 0, f = 1; char ch = getchar();
    while (ch < '0' || ch > '9') { if (ch == '-') f = -1; ch = getchar(); }
    while (ch >= '0' && ch <= '9') { x = (x << 3) + (x << 1) + ch - '0'; ch = getchar(); }
    return x * f;
}
 
int main()
{
    int t = read();
    for (int i = 1; i <= t; i++)
    {
        int n = read(), m = read(), cnt = 0, ans = 0;
        while (m--)
        {
            v[cnt++] = read();
            v[cnt++] = read() + 1;
        }
        sort(v, v + cnt);
        for (int j = 0; j < cnt; j++) ans = v[j] - ans;
        printf("Case #%d: %d\n", i, ans);
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值