首页 > 其他分享 >【luogu P5161】WD与数列(SA)(单调栈)

【luogu P5161】WD与数列(SA)(单调栈)

时间:2022-10-16 13:49:22浏览次数:86  
标签:WD sta int luogu tot height P5161 len SA

WD与数列

题目链接:luogu P5161

题目大意

给你一个序列,问你有多少对区间,长度相同,没有相交部分,而且一个区间里面所有数同时加上某个数可以变成另一个区间。

思路

首先发现它要加数那个很难处理。
那考虑一个事情其实就是直接差分一下,就变成要两个一样了。

然后再考虑,发现不能相交的条件在差分之后你就不能相邻了,你要至少隔一个数。
而且因为差分,长度为 \(1\) 的全部都被你弄掉了,要单独加上。
然后考虑怎么求两两 LCP 的和。

其实可以直接 SAM 上 rush,也可以用 SA。
用 SA 的话可以这样,用一个单调栈,因为你 LCP 在 SA 上是 height 数组的最小值,那就是所有区间的最小值的和,用单调栈维护最小值就可以。
就维护当前点作为右端点,所有左端点形成的区间的答案,然后维护这这个,每次把这个加入答案就可以。

但是你这样没有处理重合,于是考虑处理重合的部分减去。
考虑一个东西,你枚举差的位置 \(len\)。
考虑用关键点,我们把编号为 \(len\) 的倍数的都标记。
然后对于一个相邻的关键单 \(k,k+len\),我们求 \(x\in[k-len+1,k]\),然后 \(y=x+len\)。
会发现你要重合至少长度要是 \(len\),那一定会经过 \(k\) 这个点。
所以合法的 \(x\) 是 \(lcs(k,k+len)\sim k\)。
然后再求出 \(lcp(k,k+len)\),发现右边能匹配的其实固定了,所以左端点右移会使得能匹配的长度少 \(1\),所以是等差序列求和,算一下就搞定了。
(记得算上我们预先有的 \(len\))

代码

#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#define ll long long

using namespace std;

const int N = 3e5 + 100;
int n, s[N], a[N], b[N], bn, log2_[N];
ll ans;

struct SA {
	int sa[N], rnk[N], x[N], y[N], fir[N], minn[N][21], height[N];
	
	int tong[N];
	void Sort(int m, int *x, int *y) {
		for (int i = 1; i <= m; i++) tong[i] = 0;
		for (int i = 1; i <= n; i++) tong[x[i]]++;
		for (int i = 1; i <= m; i++) tong[i] += tong[i - 1];
		for (int i = n; i >= 1; i--)
			sa[tong[fir[i]]--] = y[i];
	}
	
	void Init() {
		for (int i = 1; i <= n; i++) x[i] = a[i];
		for (int i = 1; i <= n; i++) y[i] = i;
		for (int i = 1; i <= n; i++) fir[i] = x[y[i]];
		Sort(bn, x, y);
		
		int m = bn;
		for (int j = 1; j < n; j <<= 1) {
			int ynum = 0;
			for (int i = n - j + 1; i <= n; i++)
				y[++ynum] = i;
			for (int i = 1; i <= n; i++)
				if (sa[i] > j) y[++ynum] = sa[i] - j;
			for (int i = 1; i <= n; i++) fir[i] = x[y[i]];
			Sort(m, x, y);
			
			swap(x, y);
			int mm = 1;
			x[sa[1]] = 1;
			for (int i = 2; i <= n; i++)
				if (y[sa[i]] == y[sa[i - 1]] && y[sa[i] + j] == y[sa[i - 1] + j]) x[sa[i]] = mm;
					else x[sa[i]] = ++mm;
			m = mm;
			if (m == n) break;
		}
		
		int k = 0, j;
		for (int i = 1; i <= n; i++) rnk[sa[i]] = i;
		for (int i = 1; i <= n; i++) {
			if (rnk[i] == 1) {height[rnk[i]] = k; continue;}
			if (k) k--;
			j = sa[rnk[i] - 1];
			while (a[i + k] == a[j + k] && i + k <= n && j + k <= n) k++;
			height[rnk[i]] = k;
		}
		
		for (int i = 1; i <= n; i++) minn[i][0] = height[i];
		for (int i = 1; i <= 20; i++)
			for (int j = 1; j + (1 << i) - 1 <= n; j++)
				minn[j][i] = min(minn[j][i - 1], minn[j + (1 << (i - 1))][i - 1]);
	}
	
	int LCP(int x, int y) {
		x = rnk[x]; y = rnk[y];
		if (x > y) swap(x, y); x++;
		int k = log2_[y - x + 1];
		return min(minn[x][k], minn[y - (1 << k) + 1][k]);
	}
	
	int sta[N], tot;
	void work() {
		tot = 0; sta[++tot] = 1; ll now = 0;
		for (int i = 2; i <= n; i++) {
			while (tot > 1 && height[sta[tot]] > height[i]) {
				now -= 1ll * height[sta[tot]] * (sta[tot] - sta[tot - 1]);
				tot--;
			}
			sta[++tot] = i; now += 1ll * height[sta[tot]] * (sta[tot] - sta[tot - 1]);
			ans += now;
		}
	}
}T, Tf;

ll calc(int s, int t, int l) {
	int R = min(s, l) + t - 1, L = max(t, l);
	if (L <= R) return 1ll * (R - L + 1) * (L - l + 1 + R - l + 1) / 2;
	return 0;
}

int main() {
	scanf("%d", &n);
	for (int i = 1; i <= n; i++) scanf("%d", &s[i]);
	log2_[0] = -1; for (int i = 1; i <= n; i++) log2_[i] = log2_[i >> 1] + 1;
	ans = 1ll * n * (n + 1) / 2 - n;
	
	n--;
	for (int i = 1; i <= n; i++) a[i] = s[i + 1] - s[i], b[i] = a[i];
	sort(b + 1, b + n + 1); bn = unique(b + 1, b + n + 1) - b - 1;
	for (int i = 1; i <= n; i++) a[i] = lower_bound(b + 1, b + bn + 1, a[i]) - b;
	
	T.Init(); T.work();
	reverse(a + 1, a + n + 1); Tf.Init();
	reverse(a + 1, a + n + 1); 
		
	for (int k = 1; k <= n; k++)
		for (int r = k + k; r <= n; r += k) {
			int l = r - k; ans -= calc(Tf.LCP(n - r + 1, n - l + 1), T.LCP(l, r), k);
		}
	printf("%lld", ans);
	
	return 0;
}

标签:WD,sta,int,luogu,tot,height,P5161,len,SA
From: https://www.cnblogs.com/Sakura-TJH/p/luogu_P5161.html

相关文章

  • luogu P3232 [HNOI2013]游走 (期望, 高斯消元)
    https://www.luogu.com.cn/problem/P3232思路:算出每条边的期望访问次数,将期望访问次数多的赋予小的编号。一条边的期望访问次数=访问点u的期望/u的度+访问点v的期望......
  • luoguP1505旅游(处理边权的树剖)
    /*luogu1505非常简单的处理边权的树剖题。在树上将一条边定向,把这条边的权值赋给这条边的出点树剖的时候不计算lca权值即可*/#include<bits/stdc......
  • Linux系统编程15-chdir与getcwd
    #include<unistd.h>intchdir(constchar*path);作用:修改进程的工作目录比如在/home/nowcoder启动了一个可执行程序a.out,进程的工作目录就是......
  • WDA DEMO 11 根据BAPI/Function创建WDA
    货铺QQ群号:834508274进群统一修改群名片,例如BJ_ABAP_森林木。群内禁止发广告及其他一切无关链接,小程序等,进群看公告,谢谢配合不修改昵称会被不定期踢除,谢谢配合事先声明下,本......
  • WDA DEMO 03: 根据选择条件查询并显示
    下面开始干货:事先声明下,本人没参加过培训,也没看过完整的标准教程,所以一直都是野路子,土八路。所以文章中不足以及不正确的地方请大家帮忙指正。SE80新建。然后新建一个Attri......
  • P7870 「Wdoi-4」兔已着陆 题解
    大家好,由于我非常喜欢线段树,所以我用线段树切了这题。提供一种复杂度为\(\mathcal{O}(n\log^2n)\)线段树二分的做法。我们想一下,我们要用线段树来优化什么操作。我们......
  • Luogu1398
    思路什么毒瘤细节题。考虑如何解决。考虑对这些条件做出进一步的抽象。于是我们做出细致的观察。方便起见,我们横、纵坐标自左往右、自上往下标号。字母之间的关系O......
  • LuoguP2633 Count on a tree
    题意给定一棵\(n\)个节点的树,每个点有一个权值。有\(m\)个询问,每次给你\(u,v,k\),你需要回答\(u\text{xorlast}\)和\(v\)这两个节点间第\(k\)小的点权。其......
  • 【luogu CF1396D】Rainbow Rectangles(线段树)
    RainbowRectangles题目链接:luoguCF1396D题目大意给你一个L*L的矩形,里面有n个点,有各自的颜色。然后问你有多少个端点是整点的矩形可以圈出所有颜色至少有一个点。......
  • Luogu P3469 [POI2008]BLO-Blockade
    [P3469POI2008]BLO-Blockade-洛谷|计算机科学教育新生态(luogu.com.cn)图\(G\)本身联通。删除\(u\)的连边后会形成\(k\ge2\)个连通块(至少会把\(u\)隔离出......