Codeforces Round #759 F(容斥+线段树维护dp)

Posted 吃花椒的妙酱

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Codeforces Round #759 F(容斥+线段树维护dp)相关的知识,希望对你有一定的参考价值。

题目大意:给数组ai,构造b数组,使得1<=bi<=ai,且相邻数不同,求b数组的构造方案数
思路 容斥+线段树维护dp转移柿
容斥大体思想
所求 = ∑ 有 k 对 相 同 相 邻 数 ( − 1 ) k ∗ f k \\sum_有k对相同相邻数(-1)^k*f_k k(1)kfk, f k f_k fk表示至少有k对相同相邻数的方案数
我们先考虑一种方案,b1b2b3b4…bn,其中b1=b2,b3=b4,那么至少存在2对相同相邻数的方案为 ∏ i = 5 n a i ∗ m i n ( a 1 , a 2 ) ∗ m i n ( a 3 , a 4 ) \\prod_i=5^na_i*min(a_1,a_2)*min(a_3,a_4) i=5naimin(a1,a2)min(a3,a4),因第1,2个数,第三四个数字一样,其他数的选择没有限制
我们设f[i]表示前i个数有的合法方案,则有转移方程
f i = ∑ j = 0 i − 1 f j ∗ m i n ( a j + 1 , … , a i ) ( − 1 ) i − j − 1 f_i = \\sum_j=0^i-1 f_j *min(a_j+1,…,a_i) (-1)^i-j-1 fi=j=0i1fjmin(aj+1,,ai)(1)ij1这里i-j-1是因为j+1到i一共有i-j-1对相同相邻数
朴素考虑的话枚举i再枚举j是n方的转移
由于min部分对于一些后缀是不变的,我们考虑枚举i的时候,考虑ai对当前后缀的贡献,如1245再来个3,那就对后两个数有影响,对其他 f j f_j fj只有个变号的影响,这个我们放在线段树外面维护就行,线段树维护fi, ∑ f j , 以 及 m i n \\sum_f_j,以及min fjmin
注意一开始设 ∑ f 0 = 1 \\sum_f_0=1 f0=1
PS:还有更好的O(n)单调栈做法,不过没咋理解,线段树好理解一点,虽然带log2nqaq

#include<iostream>
#include<stdio.h>
#include<string.h>
#include<cstring>
#include<string>
#include<algorithm>
#include<math.h>
#include<cmath>
#include<queue>
#include<stack>
#include<vector>
#include<map>
#include<deque>
using namespace std;
typedef long long ll;
#define IOS ios::sync_with_stdio(false),cin.tie(0) 
#define _for(i,a,b) for(int i=(a) ;i<=(b) ;i++)
#define _rep(i,a,b) for(int i=(a) ;i>=(b) ;i--)
#define mst(v,s) memset(v,s,sizeof(v))
#define pii pair<int ,int >
#define AC return 0
#define fi first
#define se second
#define int long long
#define inf 0x3f3f3f3f
#define INF 0x3f3f3f3f3f3f3f3f
#define min3(a,b,c) min(a,min(b,c))
#define ls p<<1
#define rs p<<1|1
#define lson p<<1,l,mid
#define rson p<<1|1,mid+1,r
const int N = 2e5+10;
const int mod = 998244353;
const int MAXL = 60;
int n;
int a[N],dp[N];
struct ty

	int f,sum;
	int lz;
tr[N<<2];
vector<int > st;//	单调z
void pushup(int p,int l, int r)

	tr[p].sum = ( tr[ls].sum + tr[rs].sum)%mod;
	tr[p].f = (tr[ls].f + tr[rs].f)%mod;

void pushdown(int p ,int l, int r)

	int x = tr[p].lz;
	tr[p].lz=0;
	tr[ls].lz = tr[rs].lz = x;

	tr[ls].f = tr[ls].sum * x%mod;
	tr[rs].f = tr[rs].sum * x%mod; 

void insert(int p, int l, int r , int pos , int val)

	if( l==r )
	
		tr[p].f = val*tr[p].lz%mod;
		tr[p].sum = val;    
		return;
	
	if( tr[p].lz ) pushdown(p,l,r);
	int mid = (l+r)>>1;
	if( pos<=mid ) insert(lson,pos,val);
	else insert(rson,pos,val);
	pushup(p,l,r);

void change(int p, int l ,int r ,int x ,int y ,int val)

	if( x<=l && r<= y)
	
		tr[p].lz = val;
		tr[p].f =  tr[p].sum * val %mod;
		return;
	
	if( tr[p].lz ) pushdown(p,l,r);
	int mid = (l+r)>>1;
	if( y<=mid ) change(lson,x,y,val);
	else if( x>=mid+1 ) change(rson,x,y,val);
	else 
	
		change(lson,x,mid,val) , change(rson,mid+1,y,val);
	
	pushup(p,l,r);

signed main()

	IOS;
	cin>>n;
	_for(i,1,n) cin>>a[i];
	insert(1,1,n,1,1);
	_for(i,1,n)
	
		while( !st.empty() && a[i]  < a[st.back()] ) st.pop_back();
		int x = (st.empty())? 1 : st.back()+1;
		st.push_back(i);
		change(1,1,n,x,i,a[i]);
		dp[i] = ((i&1)?tr[1].f : (mod - tr[1].f)%mod);
		if( i < n )  insert(1,1,n,i+1,(i&1)?(mod-dp[i])%mod:dp[i]);
 	
 	cout<<dp[n]<<endl;
	AC;


// 3
// 2 2 2

以上是关于Codeforces Round #759 F(容斥+线段树维护dp)的主要内容,如果未能解决你的问题,请参考以下文章

Codeforces Round #341 (Div. 2) C. Wet Shark and Flowers(简单容斥)

Codeforces Round #257 (Div. 1) D - Jzzhu and Numbers 容斥原理 + SOS dp

Codeforces Round #729 (Div. 2) C. Strange Function(lcm,简单容斥)

Codeforces Round #524 (Div. 2) codeforces 1080A~1080F

Educational Codeforces Round 21 Problem F (Codeforces 808F) - 最小割 - 二分答案

Codeforces Round #797 (Div. 3) D, E, F, G题题解