首页 > 其他分享 >[题解] CFgym103069G Prof. Pang's sequence

[题解] CFgym103069G Prof. Pang's sequence

时间:2023-11-09 21:45:18浏览次数:45  
标签:Info cnt CFgym103069G add1 add0 题解 Pang ans swp

G. Prof. Pang's sequence

给一个长度为 \(n\) 的序列 \(a\),多次询问区间 \([l, r]\) 内有多少个子区间的颜色数是奇数。
\(n, m \le 10^5\)。

先按照 HH 的项链 的套路,对于每个数记一下 \(lst_i\) 表示 \(a_i\) 上一次出现的位置。然后扫描线,将所有子区间的答案转化成历史和。颜色出现次数为奇数可以对每个左端点 \(l\) 记一下 \(cnt_{0/1}\) 表示区间 \([l, r]\) 的颜色数为偶数 / 奇数。

在扫描线的过程中,右端点每右移 \(1\),对 \(cnt\) 的影响就是 \([lst_r + 1, r]\) 的 \(cnt_0\) 和 \(cnt_1\) swap 一下,对历史和的影响是 \(ans \leftarrow ans + cnt_1\)。

然后问题就转化成了维护三元组 \((cnt_0, cnt_1, ans)\),每次有操作 swap \(cnt_0\) 和 \(cnt_1\),\(ans \leftarrow ans + x \cdot cnt1\)。

手玩一下发现最后标记的形式一定是 \((cnt_0, cnt_1, ans + add_0 \times cnt_0 + add_1 \times cnt_1)\),然后分别维护就行。

时间复杂度 \(O((n + q) \log n)\)。

constexpr int MAXN = 5e5 + 9;
int n, q, a[MAXN];
int bin[MAXN], lst[MAXN];
vector<pair<int, int> > que[MAXN];
ll ans[MAXN];

struct Info {
	ll cnt[2], ans;
	
	Info() { cnt[0] = cnt[1] = 0, ans = 0; return; }
	Info(ll x, ll y, ll z) { cnt[0] = x, cnt[1] = y, ans = z; return; }
	friend Info operator + (Info x, Info y) {
		return Info(x.cnt[0] + y.cnt[0],
					x.cnt[1] + y.cnt[1],
					x.ans + y.ans);
	}
};
struct Tag {
	bool swp; ll add0, add1;
	
	Tag(): swp(false), add0(0), add1(0) { return; }
	Tag(bool x, ll a, ll b):
		swp(x), add0(a), add1(b) { return; }
	operator bool() const { return swp || add0 || add1; }
	friend Tag operator * (Tag x, Tag y) {
		Tag ans;
		if (x.swp) swap(y.add0, y.add1);
		ans.swp = x.swp ^ y.swp;
		ans.add0 = x.add0 + y.add0;
		ans.add1 = x.add1 + y.add1;
		return ans;
	}
	Info Apply(Info x) {
		if (swp) swap(x.cnt[0], x.cnt[1]);
		x.ans += add0 * x.cnt[0] + add1 * x.cnt[1];
		return x;
	}
};
struct Node {
	Info dat; Tag tag;
} sgt[MAXN << 2];

void Push_Up(int p) {
	sgt[p].dat = sgt[p << 1].dat + sgt[p << 1 | 1].dat;
	return;
}
void Push_Tag(int p, Tag tg) {
	sgt[p].tag = tg * sgt[p].tag;
	sgt[p].dat = tg.Apply(sgt[p].dat);
	return;
}
void Push_Down(int p) {
	if (sgt[p].tag) {
		Push_Tag(p << 1, sgt[p].tag);
		Push_Tag(p << 1 | 1, sgt[p].tag);
		sgt[p].tag = Tag();
	}
	return;
}
void Build(int L = 1, int R = n, int p = 1) {
	if (L == R) { sgt[p].dat.cnt[0] = 1; return; }
	int Mid = L + R >> 1; Build(L, Mid, p << 1);
	Build(Mid + 1, R, p << 1 | 1); Push_Up(p); return;
}
void Update(int l, int r, Tag tg, int L = 1, int R = n, int p = 1) {
	if (l <= L && R <= r) { Push_Tag(p, tg); return; }
	Push_Down(p); int Mid = L + R >> 1;
	if (r <= Mid) Update(l, r, tg, L, Mid, p << 1);
	else if (Mid < l) Update(l, r, tg, Mid + 1, R, p << 1 | 1);
	else Update(l, r, tg, L, Mid, p << 1), Update(l, r, tg, Mid + 1, R, p << 1 | 1);
	Push_Up(p); return;
}
ll Query(int l, int r, int L = 1, int R = n, int p = 1) {
	if (l <= L && R <= r) return sgt[p].dat.ans;
	Push_Down(p); int Mid = L + R >> 1;
	if (r <= Mid) return Query(l, r, L, Mid, p << 1);
	if (Mid < l) return Query(l, r, Mid + 1, R, p << 1 | 1);
	return Query(l, r, L, Mid, p << 1) + Query(l, r, Mid + 1, R, p << 1 | 1);
}

void slv() {
	n = Read<int>();
	for (int i = 1; i <= n; i ++) a[i] = Read<int>();
	for (int i = 1; i <= n; i ++) lst[i] = bin[a[i]], bin[a[i]] = i;
	q = Read<int>(), Build();
	for (int i = 1; i <= q; i ++) {
		int l = Read<int>(), r = Read<int>();
		que[r].emplace_back(l, i);
	}
	for (int r = 1; r <= n; r ++) {
		Update(lst[r] + 1, r, Tag(1, 0, 0));
		Update(1, r, Tag(0, 0, 1));
		for (auto _ : que[r]) {
			int l = _.fir, id = _.sec;
			ans[id] = Query(l, r);
		}
	}
	for (int i = 1; i <= q; i ++) Write(ans[i], '\n');
	return;
}

标签:Info,cnt,CFgym103069G,add1,add0,题解,Pang,ans,swp
From: https://www.cnblogs.com/definieren/p/17822615.html

相关文章

  • [CSP-J 2021] 小熊的果篮 题解
    题目链接既然只有两种东西,我们不妨分开考虑,这里也借鉴了很多二分图题目的切入点。假设苹果和桔子下标分别如下图所示:苹果:1367910桔子:2458那么第一次取,应该是这样取:1234689也就是先取开头比较小的,然后轮流取,注意一定保证递增,也就是对于苹果找出来的\(x\)......
  • [题解]CFgym103470E Paimon Segment Tree
    PaimonSegmentTree区间加,求一段时间内的区间平方和。\(n,m,q\le5\times10^4\)。对时间维差分一下,变成询问区间历史平方和。离线下来扫描线,扫描线维护时间维,数据结构维护序列维。考虑维护二元组\((a,s)\)表示当前位置值为\(a\),历史平方和为\(s\)。可以发现怎......
  • A2OJ Ladder 21 简要题解
    https://earthshakira.github.io/a2oj-clientside/server/Ladder21.html只记录Difficultylevel>=8的。有很多题是口胡的。写了的会标注提交记录。还有些很久以前写过的题就懒得搬提交记录了。71.CF444EDZYlovesplanting我们二分答案,然后可以这样转化:把权\(\ged\)的......
  • 题解:疯狂lcm
    %你赛打到一半来写个题解link:疯狂lcm题意,求:\[\sum_{i=1}^{n}lcm(i,n)\]不多说废话,直接开推:\[\begin{aligned}&=n\sum_{i=1}^{n}\frac{i}{gcd(i,n)}\\&=n\sum_{d\midn}\sum_{i=1}^{n}\frac{i}{d}[gcd(i,n)=d]\\&=n\sum_{d\midn}\sum_{i=1}^{n}......
  • KubeEdge部署 完美运行 附问题解决方法
    云端部署环境准备一、部署前工作(k8s、docker安装及配置、初始化集群、golang安装、keadm安装)配置网络参数cat>>/etc/hosts<<EOF#GitHubStart52.74.223.119github.com192.30.253.119gist.github.com54.169.195.247api.github.com185.199.111.153assets-cdn.gith......
  • linux/docker 版 Sql Server新建的数据库插入中文乱码问题解决方案
    SqlServer插入遇到乱码原因:在英文系统中,SqlServer默认排序规则为英文字典顺序解决方案一:容器版SqlServer,在创建容器时,可以加上环境变量-eMSSQL_COLLATION=Chinese_PRC_CI_AS-eTZ=Asia/Shanghai 把排序规则设为中文字典顺序并忽略大小写区分重音,时区设置为上海,不然......
  • CSP-S 2023 T1 题解
    CSP-S2023T1题解很简单,我们只需要暴力枚举五位密码,每次判断拨一个齿轮和两个齿轮能达到的状态数,如果等于\(n\),答案\(+1\)。时间复杂度\(O(10^5\times5n)\)。code#include<iostream>#include<algorithm>#include<cmath>#include<cstring>usingnamespacestd;......
  • P4069 题解
    简要题意给定一棵\(n\)个点的树,树有边权。对每个点维护一个集合\(S_u\),一开始集合均包含整数\(123456789123456789\)。设\({\rmdis}_{a,b}\)为树上两点\(a\),\(b\)的距离。共\(m\)次操作,分为如下两种:stab:设\(f\)为\(s\),\(t\)路径上的点集,对与\(\forall......
  • 上序问题解决补充(1)
    我发现好像只在idea里面整插件是不可以的你还需要下一个wampSERVER然后吧!他这个东西需要在法国官网下载,很他娘的慢,显示下载时间需要一天想不到吧我找到了中文站Wampserver3.3.064位官方版下载-WampServer中文站噔噔噔噔,闪亮登场......
  • cf908(div2)题解(补题)
    纪念这次div2让我上绿名,但还是有点遗憾,差一点就可以过三题超神了比赛链接cf908div2A这题是个骗人题,整个比赛会停下来就是一个人赢够了回合数,那么在谁这停下来就是谁赢了整个比赛,不用管每回合赢得规则。#include<iostream>usingnamespacestd;#include<string>intmain(){......