You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

186 lines
4.4 KiB

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

package class_2022_09_3_week;
// 来自360
// 有n个黑白棋子它们的一面是黑色一面是白色
// 它们被排成一行位置0~n-1上。一开始所有的棋子都是黑色向上
// 一共有q次操作每次操作将位置标号在区间[LR]内的所有棋子翻转
// 那么这个范围上的每一颗棋子的颜色也就都改变了
// 请在每次操作后求这n个棋子中黑色向上的棋子个数
// 1 <= n <= 10^18
// 1 <= q <= 300
// 0 <= 每一条操作的L、R <= n - 1
// 输出q行每一行一个整数表示操作后的所有黑色棋子的个数
// 注意 : 其实q <= 10^5也可以通过360考试时候降低了难度
public class Code01_BlackWhiteChess {
// 为了测试
// 暴力方法
public static class Right {
boolean[] white;
public Right(long n) {
white = new boolean[(int) n];
}
public void reverse(long l, long r) {
if (l <= r) {
for (int i = (int) l; i <= (int) r; i++) {
white[i] = !white[i];
}
}
}
public long blacks() {
long ans = 0;
for (boolean s : white) {
ans += !s ? 1 : 0;
}
return ans;
}
}
// 正式结构的实现
// 动态开点线段树
// 1 ~ 10^18 -> node
// l ~ r -> node
// l ~ r -> sum(黑子的数量)
// l ~ r -> 当前有没有翻转的动作需要往下传
public static class Node {
public long sum;
public boolean change;
public Node left;
public Node right;
public Node(long len) {
sum = len;
change = false;
}
}
// n = 10^18
// DynamicSegmentTree dst = new DynamicSegmentTree(n);
// int[] c1 = {4, 4000万} dst.reverse(c1[0], c1[1]) -> dst.blacks
// int[] c2 ...
// ...
// c1 [l, r] 翻转, 数量 1~n
// c2 [l, r] 翻转, 数量 1~n
public static class DynamicSegmentTree {
// 1 ~ n
public Node root;
public long size;
public DynamicSegmentTree(long n) {
root = new Node(n);
size = n;
}
public long blacks() {
return root.sum;
}
// l++ r++
// 0, 7 -> 1,8
// 4, 19 -> 5, 20
// 19, 4 -> 不操作
public void reverse(long l, long r) {
if (l <= r) {
l++;
r++;
reverse(root, 1, size, l, r);
}
}
// l...r 线段树范围 s...e 任务范围
// Node cur
private void reverse(Node cur, long l, long r, long s, long e) {
if (s <= l && r <= e) {
cur.change = !cur.change;
cur.sum = (r - l + 1) - cur.sum;
} else {
long m = (l + r) >> 1;
pushDown(cur, m - l + 1, r - m);
if (s <= m) {
reverse(cur.left, l, m, s, e);
}
if (e > m) {
reverse(cur.right, m + 1, r, s, e);
}
pushUp(cur);
}
}
private void pushDown(Node cur, long ln, long rn) {
if (cur.left == null) {
cur.left = new Node(ln);
}
if (cur.right == null) {
cur.right = new Node(rn);
}
if (cur.change) {
cur.left.change = !cur.left.change;
cur.left.sum = ln - cur.left.sum;
cur.right.change = !cur.right.change;
cur.right.sum = rn - cur.right.sum;
cur.change = false;
}
}
private void pushUp(Node cur) {
cur.sum = cur.left.sum + cur.right.sum;
}
}
public static void main(String[] args) {
int N = 1000;
int testTimes = 5000;
int opTimes = 500;
System.out.println("功能测试开始");
for (int i = 0; i < testTimes; i++) {
int n = (int) (Math.random() * N) + 1;
Right right = new Right(n);
DynamicSegmentTree dst = new DynamicSegmentTree(n);
boolean pass = true;
for (int j = 0; j < opTimes; j++) {
int a = (int) (Math.random() * n);
int b = (int) (Math.random() * n);
int l = Math.min(a, b);
int r = Math.max(a, b);
right.reverse(l, r);
dst.reverse(l, r);
if (right.blacks() != dst.blacks()) {
pass = false;
return;
}
}
if (!pass) {
System.out.println("出错了!");
break;
}
}
System.out.println("功能测试结束");
System.out.println("性能测试开始");
long n = 1000000000000000000L;
int ops = 100000;
System.out.println("数组范围 : " + n);
System.out.println("查询次数 : " + ops);
DynamicSegmentTree dst = new DynamicSegmentTree(n);
long start = System.currentTimeMillis();
for (int j = 0; j < ops; j++) {
long a = (long) (Math.random() * n);
long b = (long) (Math.random() * n);
long l = Math.min(a, b);
long r = Math.max(a, b);
dst.reverse(l, r);
}
long end = System.currentTimeMillis();
System.out.println("运行时间 : " + (end - start) + " 毫秒");
System.out.println("性能测试结束");
}
}