标签:重复 define include 表示 its using 方便 += 容斥
N 个小朋友围成一圈,你有无穷个糖果,想把其中一些分给他们。
从某个小朋友开始,我们顺时针给他们标号为 1 ~ N 。第\(i\)个小朋友可以得到至多 \(a[i]\),至少 1 个糖果。
问有多少种分配方案使得每一对相邻的小朋友拿到的糖果数不同。答案对\(10^9+7\)取模。
第一行一个整数 N。
接下来一行 N 个整数,第 i 个数表示 a[i]。
一行一个整数,表示答案。
3
3 3 3
6
4
4 4 4 4
84
\[n≤10^6,a[i]≤10^9\]
题解
================
\(f[i]\) 为 \([1,i]\) 满足条件的方案数,先考虑一条链,那么由\(f[i?1]\)递推到\(f[i]\)的公式为
\[f[i?1]?A[i]f[i?1]?A[i]\]但是这样显然会有重复的,而且只存在第i项和第i-1项重复的情况,那就减去\[f[i?2]?min(A[i],A[i?1])\]但是这样又会把第i项,第i-1项,第i-2项都相同的情况给减掉,所以再加上\[f[i?3]?min(A[i],A[i?1],A[i-2])\]所以得到
\[f[i]=\sum_{j=0}^{j<i}f[j]*min(A[i],A[i-1],\dots,A[j+1])*(-1)^{i-j-1}\]
\[f[i]=(-1)^{i}*\sum_{j=0}^{j<i}f[j]*min(A[i],A[i-1],\dots,A[j+1])*(-1)^{j+1}\]
我们发现\[min(A[i],A[i-1],\dots,A[j+1])\]
发现后面很多段f[i]的系数是一个值,而且这个值是按位置递增的,可以用单调栈搞一下。
怎么搞到环上?减去第一段和最后一段相等的情况。
为了方便,让第一段为最小的元素,这样可以用\(a1\)统计方案了
这里我没有用到特别容斥的方案来理解
首先,长度为2的链直接就是一个环了
长度为3的链减去长度为2的环就可以拼成环了
原理是对第三个位置填上和第一个位置相等的元素,每种情况只能填一个,\(a1\)最小保证一定有的填
类推 长度为\(N\)的链减去一个长度为\(n?1\)的环可以拼成一个长为\(n\)的环,复杂度\(O(n)\)
#include <bits/stdc++.h>
#define int long long
#define re register int
#define inf 1e18
using namespace std;
inline void read(int &x){
x=0;char ch=getchar();
for(;!isdigit(ch);ch=getchar());
for(; isdigit(ch);ch=getchar())
x=(x<<1)+(x<<3)+(ch^48);
}
const int N=1000010,P=1e9+7;
int a[N],b[N],mn[N],f[N],dp[N],s[N],n,p,cnt,ans,sum;
signed main(){
read(n),a[0]=inf;
for(re i=1;i<=n;++i)read(a[i]),p=(a[i]<a[p])?i:p;
while(cnt!=n)b[++cnt]=a[p],p=p%n+1;p=0;
f[1]=dp[0]=P-1,dp[1]=b[1],p=s[1]=1;
for(re tmp=0,i=2;i<=n;++i,tmp=0){
while(p&&b[s[p]]>=b[i])(tmp+=f[s[p--]])%=P;
if(p) dp[i]=(s[p]&1?(P-dp[s[p]]):dp[s[p]]);
(dp[i]+=tmp*b[i]%P)%=P;
(dp[i]+=(dp[i-1]*b[i]%P*(i&1?-1:1)+P))%=P;
dp[i]=(dp[i]*(i&1?-1:1)+P)%P;
f[s[++p]=i]=(tmp+dp[i-1]*(i&1?-1:1)+P)%P;
}
// for(re i=1;i<=n;++i) printf("f:%lld\n",f[i]);
// for(re i=1;i<=n;++i) printf("dp:%lld\n",dp[i]);
for(re i=n;i>=2;--i) (ans+=((n-i)&1?P-dp[i]:dp[i]))%=P;
printf("%lld",ans);
return 0;
}
标签:重复 define include 表示 its using 方便 += 容斥
原文地址:https://www.cnblogs.com/Sparks-Pion/p/9693808.html