Non-equal Neighbours(容斥/dp/单调栈/前缀和/好题)

题目

题意:给定一个长度为n的正整数数组 a i a_i ai,求有多少数组 b i b_i bi,满足 1 < = b i < = a i , b i ! = b i + 1 1<=b_i<=a_i,b_i!=b_{i+1} 1<=bi<=ai,bi!=bi+1 1 < = n < = 2 e 5 , 1 < = a i < = 2 e 9 1<=n<=2e5,1<=a_i<=2e9 1<=n<=2e5,1<=ai<=2e9

另一种思路
思路:
对于长度为 n n n的数组,如果它有 k k k b i = = b i + 1 b_i==b_{i+1} bi==bi+1的元素,那么相应的,它有 n − 1 − k n-1-k n1k b i ! = b i + 1 b_i!=b_{i+1} bi!=bi+1的位置,它相当于被切割成 n − k n-k nk段,段内元素相等。

f [ i ] [ x ] f[i][x] f[i][x]表示取前i个元素,且被分割为 x x x段的b数组个数。则 f [ i ] [ x ] = f [ p o s ] [ x ] + ∑ j = p o s i − 1 f [ j ] [ x − 1 ] ∗ a [ i ] f[i][x]=f[pos][x]+\sum_{j=pos}^{i-1}f[j][x-1]*a[i] f[i][x]=f[pos][x]+j=posi1f[j][x1]a[i],其中 p o s pos pos表示前 i − 1 i-1 i1个元素中,从右到左,第一个小于 a i a_i ai的元素的下标(可以用单调栈来维护)。此转移方程很好理解,第一部分 f [ p o s ] [ x ] f[pos][x] f[pos][x]表示 b p o s + 1 , b p o s + 2 , . . . , b i b_{pos+1},b_{pos+2},...,b_i bpos+1,bpos+2,...,bi取和 b p o s b_{pos} bpos相同的值,此时段数不变;第二部分表示 b j + 1 , . . . , b i − 1 b_{j+1},...,b_{i-1} bj+1,...,bi1取和 b i b_i bi相同的值,此时新增了一段连续相同的值(与 b j b_j bj不同)。

但我们发现, f [ i ] [ x ] f[i][x] f[i][x]在计算过程中,也包含了 f [ i ] [ x − 1 ] f[i][x-1] f[i][x1]。我们在计算 ∑ j = p o s i − 1 f [ j ] [ x − 1 ] ∗ a [ i ] \sum_{j=pos}^{i-1}f[j][x-1]*a[i] j=posi1f[j][x1]a[i]时,实际上也算进了 b i = = b j b_i==b_j bi==bj的场景。因此,我们需要减去 f [ i ] [ x − 1 ] f[i][x-1] f[i][x1]。所以,有
r e a l _ f [ n ] [ n ] = f [ n ] [ n ] − r e a l _ f [ n ] [ n − 1 ] real\_f[n][n]=f[n][n]-real\_f[n][n-1] real_f[n][n]=f[n][n]real_f[n][n1]
r e a l _ f [ n ] [ n − 1 ] = f [ n ] [ n − 1 ] − r e a l _ f [ n ] [ n − 2 ] real\_f[n][n-1]=f[n][n-1]-real\_f[n][n-2] real_f[n][n1]=f[n][n1]real_f[n][n2]

r e a l _ f [ n ] [ 1 ] = f [ n ] [ 1 ] − r e a l _ f [ n ] [ 0 ] real\_f[n][1]=f[n][1]-real\_f[n][0] real_f[n][1]=f[n][1]real_f[n][0]
r e a l _ f [ n ] [ 0 ] = f [ n ] [ 0 ] real\_f[n][0]=f[n][0] real_f[n][0]=f[n][0]
这是一个容斥过程
r e a l _ f [ n ] [ n ] = f [ n ] [ n ] − f [ n ] [ n − 1 ] + f [ n ] [ n − 2 ] − . . . real\_f[n][n]=f[n][n]-f[n][n-1]+f[n][n-2]-... real_f[n][n]=f[n][n]f[n][n1]+f[n][n2]...

我们可以发现, f [ n ] [ x ] f[n][x] f[n][x]的x,如果x和n奇偶性相同,则为正贡献;否则为负贡献。

重新定义 f [ i ] [ x ] f[i][x] f[i][x],取前i个数,且段数个数为奇数(x=1)或偶数(x=0)的个数。
则答案为 f [ n ] [ 0 ] − f [ n ] [ 1 ] f[n][0]-f[n][1] f[n][0]f[n][1](n为偶数)或 f [ n ] [ 1 ] − f [ n ] [ 0 ] f[n][1]-f[n][0] f[n][1]f[n][0](n为奇数)。

贴上zltzlt大佬的代码

/*
 
p_b_p_b txdy
AThousandMoon txdy
AThousandSuns txdy
hxy txdy
 
*/
 
#include <bits/stdc++.h>
#define pb push_back
#define fst first
#define scd second
 
using namespace std;
typedef long long ll;
typedef pair<ll, ll> pii;
 
const int maxn = 200100;
const ll mod = 998244353;
 
ll n, a[maxn], st[maxn], top;
ll f[maxn][2], g[maxn][2];
 
void solve() {
	scanf("%lld", &n);
	for (int i = 1; i <= n; ++i) {
		scanf("%lld", &a[i]);
	}
	f[0][0] = g[0][0] = 1;
	for (int i = 1; i <= n; ++i) {
		while (top && a[i] <= a[st[top]]) {
			--top;
		}
		if (top) {
			f[i][0] = (f[st[top]][0] + (g[i - 1][1] - g[st[top] - 1][1] + mod) * a[i]) % mod;
			f[i][1] = (f[st[top]][1] + (g[i - 1][0] - g[st[top] - 1][0] + mod) * a[i]) % mod;
		} else {
			f[i][0] = g[i - 1][1] * a[i] % mod;
			f[i][1] = g[i - 1][0] * a[i] % mod;
		}
		g[i][0] = (g[i - 1][0] + f[i][0]) % mod;
		g[i][1] = (g[i - 1][1] + f[i][1]) % mod;
		st[++top] = i;
	}
	printf("%lld", (n & 1) ? (f[n][1] - f[n][0] + mod) % mod : (f[n][0] - f[n][1] + mod) % mod);
}
 
int main() {
	int T = 1;
	// scanf("%d", &T);
	while (T--) {
		solve();
	}
	return 0;
}
//By zltzlt,
// http://codeforces.com/profile/zltzlt 
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值