码迷,mamicode.com
首页 > 其他好文 > 详细

容斥题【1】

时间:2018-09-24 00:27:16      阅读:202      评论:0      收藏:0      [点我收藏+]

标签:重复   define   include   表示   its   using   方便   +=   容斥   

题目描述

N 个小朋友围成一圈,你有无穷个糖果,想把其中一些分给他们。
从某个小朋友开始,我们顺时针给他们标号为 1 ~ N 。第\(i\)个小朋友可以得到至多 \(a[i]\),至少 1 个糖果。
问有多少种分配方案使得每一对相邻的小朋友拿到的糖果数不同。答案对\(10^9+7\)取模。

输入描述:

第一行一个整数 N。
接下来一行 N 个整数,第 i 个数表示 a[i]。

输出描述:

一行一个整数,表示答案。

  • 输入1

3
3 3 3
  • 输出1

6
  • 输入2

4
4 4 4 4
  • 输出2

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)\)

code

#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;
}

容斥题【1】

标签:重复   define   include   表示   its   using   方便   +=   容斥   

原文地址:https://www.cnblogs.com/Sparks-Pion/p/9693808.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!