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)k∗fk,
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=5nai∗min(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=0i−1fj∗min(aj+1,…,ai)(−1)i−j−1这里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
∑fj,以及min
注意一开始设
∑
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) - 最小割 - 二分答案