首页 > 其他分享 >题解 [ARC153B] Grid Rotations

题解 [ARC153B] Grid Rotations

时间:2023-07-17 21:33:58浏览次数:33  
标签:lazy int 题解 void Rotations Grid ARC153B siz id

[ARC153B] Grid Rotations

有思维含量的一题。

我们横纵坐标分开考虑,对于每一个矩形,每次操作会使其内部元素的横坐标上下对调。

纵坐标也同理,左右对调。

而这种反转操作我们显然可以直接用两棵文艺平衡树维护,复杂度 \(O(n\log n)\)。

标程的做法更巧妙一下。我们可以把一条链收尾相接,两段序列的反转就相当于圆的反转。

所以我们可以只定位其中两个点,然后根据其最终位置填补出剩下元素。复杂度 \(O(n)\)。

代码是第一种做法。

#include <bits/stdc++.h>
using namespace std;
const int N = 5e5 + 5;
int n, m, q, a[N], b[N], rt = 0, x[N], y[N], tot = 0;
char c[N];
struct node{
	int l, r, siz, v, lazy, pri;
}t[N];
int d(int i, int j) {
	return (i - 1) * m + j;
}
void add(int x) {t[x].v = x; t[x].pri = rand(); t[x].siz = 1; t[x].l = t[x].r = 0;}
void pushdown(int id) {
	if (t[id].lazy == 0) return;
	swap(t[id].l, t[id].r);
	t[t[id].l].lazy ^= 1;
	t[t[id].r].lazy ^= 1;
	t[id].lazy = 0;
}
void pushup(int id) {
	t[id].siz = t[t[id].l].siz + t[t[id].r].siz + 1;
}
void split(int id, int k, int &l, int &r) {
	if (id == 0) {
		l = r = 0;
		return;
	}
	pushdown(id);
	int tmp = t[t[id].l].siz + 1;
	if (tmp <= k) {
		l = id;
		split(t[id].r, k - tmp, t[id].r, r);
	} else {
		r = id;
		split(t[id].l, k, l, t[id].l);
	}
	pushup(id);
}
int merge(int l, int r) {
	if (!l || !r) return l + r;
	if (t[l].pri < t[r].pri) {
		pushdown(l);
		t[l].r = merge(t[l].r, r);
		pushup(l);
		return l;
	} else {
		pushdown(r);
		t[r].l = merge(l, t[r].l);
		pushup(r);
		return r;
	}
}
void dfs1(int u) {
	pushdown(u);
	if (t[u].l) dfs1(t[u].l);
	x[++tot] = t[u].v;
	if (t[u].r) dfs1(t[u].r);
}
void dfs2(int u) {
	pushdown(u);
	if (t[u].l) dfs2(t[u].l);
	y[++tot] = t[u].v;
	if (t[u].r) dfs2(t[u].r);
}
void huan(int l, int r) {
	int t1, t2, t3, tt;
	split(rt, r, t1, t2);
	split(t1, l - 1, t1, t3);
	t[t3].lazy ^= 1;
	rt = merge(merge(t1, t3), t2);
}
int main() {
	srand(time(0));
	ios::sync_with_stdio(false); cin.tie(0); cout.tie(0);
	cin >> n >> m;
	for (int i = 1; i <= n; ++i) {
		for (int j = 1; j <= m; ++j) {
			cin >> c[d(i, j)];
		}
	}
	for (int i = 1; i <= n; ++i) {
		add(i);
		rt = merge(rt, i);
	}
	cin >> q;
	for (int i = 1; i <= q; ++i) {
		cin >> a[i] >> b[i];
		huan(1, a[i]); huan(a[i] + 1, n);
	}
	dfs1(rt);
	rt = tot = 0;
	for (int i = 1; i <= m; ++i) {
		add(i);
		rt = merge(rt, i);
	}
	for (int i = 1; i <= q; ++i) {
		huan(1, b[i]); huan(b[i] + 1, m);
	}
	dfs2(rt);
	for (int i = 1; i <= n; ++i) {
		for (int j = 1; j <= m; ++j) {
			cout << c[d(x[i], y[j])];
		}
		cout << endl;
	}
	return 0;
}

标签:lazy,int,题解,void,Rotations,Grid,ARC153B,siz,id
From: https://www.cnblogs.com/HQJ2007/p/17561295.html

相关文章

  • 题解 Yet Another Minimization Problem
    YetAnotherMinimizationProblem神仙题。第一眼看上去就是DP。定义\(f_{i,j}\)表示当前点\(i\),分\(j\)段的最小费用。\(f_{i,j}=\min(f_{i,j},f_{k,j-1}+val_{k+1,i})\)然后发现复杂度\(O(n^2k)\),直接T飞,需要优化。我们发现\(j\)那一维可以滚掉,也就是只考虑第......
  • 题解 P4322 [JSOI2016]最佳团体
    P4322[JSOI2016]最佳团体分数规划+树形背包。可以根据推荐关系建出一颗树,然后如果选了一点,则该点到根上的所有点都必须选。二分\(mid\),定义每个结点的权值,然后判断选\(k+1\)个节点的最大值是否大于\(0\)。设\(f_{i,j}\)为当前节点\(i\),在其子树内选了\(j\)个节点,最......
  • 题解 Bracket Insertion
    BracketInsertion神仙DP题,不愧是tourist。容易发现,括号序列一共有\(1\cdot3\cdot5...\cdot(2n-1)\)种生成方式。假如(为\(1\),)为\(-1\),那么一个序列合法的充要条件为:最小前缀和为\(0\),且以\(0\)结尾。现在考虑维护这些前缀和。如果我们在当前序列某一位后插......
  • 题解 P2276 [HNOI2002]农场的果树
    首先可以观察出一颗\(n\)个节点的二叉树,当其字典序最小的时候,其形态为一条向右偏的链,当其字典序最大的时候,是一条向左偏的链。由于每一种编码对应唯一的一颗二叉树,我们可以先建树。然后考虑树上分治,尝试以下三种方式:变大右子树的字典序变大左子树的字典序,并将右子树变成......
  • 题解 P7640 [BalticOI 2006 Day 2] CITY PLANNING
    首先我们定义“圈”为与原点距离相等的点集。...3.....323...32123.3210123.32123...323.....3...暴力:把圈放到堆里,然后每次取出代价最小的一圈,修改当前圈的楼层,向外圈拓展。正解:考虑二分。如果是二分最终答案,我们会发现......
  • 题解 P7250 [BalticOI 2012 Day1] 山峰
    通过观察,可以发现此题和最小生成树十分相似(两个地点之间途经的最小值最大)。于是可以考虑这么做:通过bfs将每一个块预处理出来,并记录其编号、高度、类型(是否为高地)以及边缘的点。将每一个块按高度从大到小排序。依次枚举每个块:对于当前要处理的块,枚举其边界的所有点,......
  • 题解 AT3726 [ARC087B] FT Robot
    首先可以观察到一个非常重要的性质:对于一次前进的操作,如果前面有奇数次转向,则走上下,否则走左右。(当然如果一开始就前进就只能走右)于是我们可以将其拆成许多的“块”,并分成两类,即前进方向为左右还是上下。然后对于两个维度分别dp。\(f_{i},_{j}=f_{i-1},_{j-val}\|\f_{i-......
  • winform中DataGridview控件
    privatevoidForm1_Load(objectsender,EventArgse){DataTabledt=newDataTable();DataColumnc1=newDataColumn("序号",typeof(string));DataColumnc2=newDataColumn("名称",typeof(string));......
  • Charles抓取https请求及常见问题解决
    一、背景APP测试的时候,通常都需要通过抓包工具抓取各类请求,查看接口的入参、返回值等,用于分析定位问题。常用的抓包工具有fiddler、charles等,抓取http的请求比较简单,https的请求稍显复杂。由于更喜欢charles的页面风格,本篇文章主要介绍以下两点:1、Charles如何抓取电脑端和手机端的......
  • C#DataGridView两个数据表同步滚动
    一、同步滚动SumTable为表1,CycleTable为表2两个表都添加Scroll滚动事件privatevoidSumTable_Scroll(objectsender,ScrollEventArgse)//滚动同步{CycleTable.FirstDisplayedScrollingRowIndex=SumTable.FirstDisplayedScrollingRowIndex;......