首页 > 其他分享 >[NOI2007] 项链工厂 题解

[NOI2007] 项链工厂 题解

时间:2024-07-19 14:45:16浏览次数:6  
标签:return idx int 题解 tree mov NOI2007 tag 项链

前言

题目链接:洛谷Hydro & bzoj

题意简述

yzh 喜欢写 DS 题!你要维护一个环:

  1. 顺时针移动 \(k\) 位;
  2. 翻转 \(2 \sim n\);
  3. 交换 \(i\) 与 \(j\);
  4. 区间覆盖;
  5. 查询整个环有几个颜色段;
  6. 查询 \(i \sim j\) 有几个颜色段。

题目分析

平衡树板子啊,代码很好写,\(273\) 行。但是为什么不使用线段树呢?

发现,顺时针移位,原本该连续的区间也还在一块(这里指的是环上,在序列上可能一个在首一个在尾,但这对分析问题并不重要)。所以遇到移位操作,只用记录一个偏移量,查询的时候对下表进行相应处理即可。同理,翻转操作也不必真的进行翻转,只用记一个翻转标记,每次让它异或 \(1\) 即可。我们可以轻松写出如下转换函数。

auto trans = [&mov, &flip] (int x) -> int {
	if (flip) x = mov - x + 2;
	else x = x - mov;
	x = (x % n + n) % n;
	return x ? x : n;
};

接下来考虑线段树如何实现。我们发现,问题变成了区间覆盖、单点查询颜色、区间查询颜色段数。很套路,在信息中记录左右端点的颜色以及区间内的颜色段数。合并的时候把两个自区间颜色段数相加,如果左边的右端点和右边的左端点颜色相同,再减去一次重复算的这一次。

struct Info {
	int l, r, cnt;
	friend Info operator + (const Info& a, const Info& b) {
		return { a.l, b.r, a.cnt + b.cnt - (a.r == b.l) };
	}
};

剩下的板子不展开。

代码

// #pragma GCC optimize(3)
// #pragma GCC optimize("Ofast", "inline", "-ffast-math")
// #pragma GCC target("avx", "sse2", "sse3", "sse4", "mmx")
#include <iostream>
#include <cstdio>
#define debug(a) cerr << "Line: " << __LINE__ << " " << #a << endl
#define print(a) cerr << #a << "=" << (a) << endl
#define file(a) freopen(#a".in", "r", stdin), freopen(#a".out", "w", stdout)
#define main Main(); signed main() { return ios::sync_with_stdio(0), cin.tie(0), Main(); } signed Main
using namespace std;

int n, m, col[500010];

struct Segment_Tree {
	#define lson (idx << 1    )
	#define rson (idx << 1 | 1)
	
	struct Info {
		int l, r, cnt;
		friend Info operator + (const Info& a, const Info& b) {
			if (a.l == -1) return b;
			if (b.l == -1) return a;
			return { a.l, b.r, a.cnt + b.cnt - (a.r == b.l) };
		}
	};
	
	struct node {
		int l, r;
		int tag;
		Info info;
	} tree[500010 << 2];
	
	void build(int idx, int l, int r) {
		tree[idx] = {l, r, -1, {0, 0, 0}};
		if (l == r) return tree[idx].info = {col[l], col[r], 1}, void();
		int mid = (l + r) >> 1;
		build(lson, l, mid);
		build(rson, mid + 1, r);
		tree[idx].info = tree[lson].info + tree[rson].info;
	}
	
	void pushtag(int idx, int tag) {
		tree[idx].tag = tag;
		tree[idx].info = {tag, tag, 1};
	}
	
	void pushdown(int idx) {
		if (tree[idx].tag == -1) return;
		pushtag(lson, tree[idx].tag);
		pushtag(rson, tree[idx].tag);
		tree[idx].tag = -1;
	}
	
	void modify(int idx, int l, int r, int tag) {
		if (tree[idx].l > r || tree[idx].r < l) return;
		if (l <= tree[idx].l && tree[idx].r <= r) return pushtag(idx, tag);
		pushdown(idx);
		modify(lson, l, r, tag);
		modify(rson, l, r, tag);
		tree[idx].info = tree[lson].info + tree[rson].info;
	}
	
	Info query(int idx, int l, int r) {
		if (tree[idx].l > r || tree[idx].r < l) return {-1, -1, 0};
		if (l <= tree[idx].l && tree[idx].r <= r) return tree[idx].info;
		pushdown(idx);
		return query(lson, l, r) + query(rson, l, r);
	}
	
	#undef lson
	#undef rson
} yzh;

#ifdef XuYueming
#define printf printf(">>> "), printf
#endif

signed main() {
	scanf("%d%d", &n, &m);
	for (int i = 1; i <= n; ++i)
		scanf("%d", &col[i]);
	scanf("%d", &m), yzh.build(1, 1, n);
	for (int i, j, c, k, mov = 0, flip = 0; m--; ) {
		static char op[10];
		static auto trans = [&mov, &flip] (int x) -> int {
			if (flip) x = mov - x + 2;
			else x = x - mov;
			x = (x % n + n) % n;
			return x ? x : n;
		};
		scanf("%s", op);
		if (*op == 'R') {
			scanf("%d", &k);
			mov = (mov + k) % n;
		} else if (*op == 'F') {
			flip ^= 1, mov = (n - mov) % n;
		} else if (*op == 'S') {
            scanf("%d%d", &i, &j);
			i = trans(i), j = trans(j);
			int ci = yzh.query(1, i, i).l, cj = yzh.query(1, j, j).l;
			yzh.modify(1, i, i, cj), yzh.modify(1, j, j, ci);
        } else if (*op == 'P') {
            scanf("%d%d%d", &i, &j, &c);
			i = trans(i), j = trans(j);
			if (flip) swap(i, j);
			if (i <= j) {
				yzh.modify(1, i, j, c);
			} else {
				yzh.modify(1, i, n, c);
				yzh.modify(1, 1, j, c);
			}
        } else if (op[1] == '\0') {
			printf("%d\n", max(1, yzh.tree[1].info.cnt - (yzh.tree[1].info.l == yzh.tree[1].info.r)));
        } else {
            scanf("%d%d", &i, &j);
			i = trans(i), j = trans(j);
			if (flip) swap(i, j);
			if (i <= j) {
				printf("%d\n", yzh.query(1, i, j).cnt);
			} else {
				printf("%d\n", (yzh.query(1, i, n) + yzh.query(1, 1, j)).cnt);
			}
        }
	}
	return 0;
}

后记 & 反思

没有敏锐地发现连续段在操作后还是连续的这一性质,导致没秒掉这道水题。

标签:return,idx,int,题解,tree,mov,NOI2007,tag,项链
From: https://www.cnblogs.com/XuYueming/p/18311407

相关文章

  • 1004:字符三角形 题解
    题目链接题目描述给定一个字符,用它构造一个底边长\(5\)个字符,高\(3\)个字符的等腰字符三角形。解题思路由于是字符,我们需要定义一个char类型的字符变量。第一行为两个空格和一个字符第二行为一个空格和三个字符第三行是五个字符输出即可ACCode#include<bits/stdc++.h......
  • 1003:对齐输出 题解
    题目链接题目描述读入三个整数,按每个整数占\(8\)个字符的宽度,右对齐输出它们,按照格式要求依次输出三个整数,之间以一个空格分开。解题思路由于我们不知道这个数有多大,所以我们可以用printf自带的占位符%xd输出,其中x为位数。例:printf("%3d",a);就是占用3位。题目要求为\(8\)位......
  • 1001:Hello,World! 题解
    题目链接题目描述编写一个能够输出“\(\mathtt{Hello,World!}\)”的程序,这个程序常常作为一个初学者接触一门新的编程语言所写的第一个程序,也经常用来测试开发、编译环境是否能够正常工作。提示:“\(\mathtt{Hello,World!}\)”中间没空格。解题思路梦开始的地方\(Ver3.0\)没......
  • 暑假集训CSP提高模拟1考试题解
    A.Start洛谷原题链接一道大模拟,赛时20pts。教授の高光时刻-输出没加句号、空格。-C++向0取整。-DOUBLE没传递。--9操作成-1(复制粘贴导致的)。-负数位运算卡常。其实这题还是比较简单的,细节在题目中讲的很详细,跟着它说的去做就好了。我的方法是把每个玩家用一个结构......
  • [AGC012E] Camel and Oases 题解
    题目链接题目链接题目解法可能并没有那么难(?首先\(V\)的取值只有\(\logV\)种,即\(\lfloor\frac{V}{2^k}\rfloor\)称\(\lfloor\frac{V}{2^k}\rfloor\)为第\(k\)层,先预处理出每一层的极大连通区间我们可以把问题抽象成:每一层中选一个区间,要求覆盖\([1,n]\),且第\(0......
  • 2061:【例1.2】梯形面积 题解
    题目描述在梯形中阴影部分面积是150平方厘米,求梯形面积。解题思路简单的数学题。三角形面积公式为\(S=\frac{ah}{2}\),反推可得\(h=\frac{2S}{a}\),将\(a=15cm\)和\(S=150cm^2\)代入公式\(h=\frac{2S}{a}\),解得\(h=20cm\),又由于\(h_{▲}=h_{梯形}\),所以\(h_{梯形}=h_{▲}=20c......
  • 2024牛客暑期多校训练营2 B.MST(题解)
    题意给一张\(n\)个点,\(m\)条边的无向图,\(q\)次询问,每次询问给\(k\)个结点,问这\(k\)个结点的诱导子图(也就是原图中抽出这些结点,以及原图中这些节点之间有的边)的最小生成树是多少,不连通输出-1,保证\(q\)次询问加起来问到的点的数量\(\sumk_i\leq10^5\)。思路......
  • 题解:CF1381A1 Prefix Flip (Easy Version)
    思路这道题直接用下一题的代码就行了\(C1\):注意到限制\(3n\)很大,于是看每一位是不是一样的,再操作,如样例一:转化第一位:\(01\to11\)。转化第二位:\(11\to00\to10\)。每次把当前位子提到第一位,然后翻转第一位,最后翻转回去,最多\(3n\)次,不用暴力操作直接计答案时间复杂度......
  • 牛客多校H题题解
    链接:[https://ac.nowcoder.com/acm/contest/81597/H]来源:牛客网题目描述Redstandsatthecoordinate\((0,0)\)oftheCartesiancoordinatesystem.Shehasastringofinstructions:up,down,left,right(where'right'increasesthex-coordinateby\(1......
  • 题解
    A-地毯标准的二维差分前缀和,定义\(s_{i,j}\)为当前格子的权值,然后根据题目模拟题意进行差分求和即可#include<bits/stdc++.h>#defineintlonglongusingnamespacestd;constintN=1e3+10,mod=1e9+7;ints[N][N];signedmain(){std::ios::sync_with_......