容斥做题笔记

凸轮题

\[F[n]=\sum \limits _{i\subsetneqq n} G_i*F_{n\oplus i}*(|i|!)*(-1)^{|i|+1}/|n|\]

Description

现在有一个 n 个节点的图,n*(n-1)/2 条边的无向完全图,图中不可能包含自环和重边.每条边没有边权.
我们希望给这个图染色,从而使每条边连接的点颜色不同.
然而现在采用了坠新的64位超仿真彩色显示,每个点的颜色是渐变的一个区间,这使得染色的方案数大大增多.
不过 Komachi并没有放弃,他还想知道个图的可行染色方案数在模1000000007下是多少.

化简题意

给定n个集合,每个集合中有若干元素.
现在要求在每个集合中取出一个元素,要求每个集合中取出的元素均不同,求方案数.
\(n\leq 15\)

Solution

  • 考虑一种\(3^n\)的做法.
  • 首先记G若干集合取出元素皆相同的方案数.
  • 然后记F若干集合取出元素均不相同的方案数.
  • 考虑转移.
  • 发现对于一个\(G[i]*F[n\oplus i]\),表示的颜色最多增加一个.
  • 对于最后的一种不合法方案:\(111123\) 是不能记录答案的
  • 现在研究那些状态可以转移过来: \(100023, 010023, 001023, 000123, 000023\) 对于前4种,方案数可以表示为i,i表示最后重复数字的个数. 而最后一种只能从一种状态转移过来.
  • 所以在计算贡献时,有乘\(|i!|\),用于消元重复方案.
  • 再分析合法情况\(123456\),只能从\(023456,103456,120456,123056,123406,123450\). 就是\(|n|\)种方案.
  • 所以最后要除以\(|n|\)
  • 其实上面的代码还有一种优化方法,虽然已经不在容斥的考虑范围之内.
  • 发现上面的公式是一个典型的子集卷积,所以可以套用\(FWT\)\(O(2^nn^2)\)的复杂度下求解.
  • 大致流程是,记\(F_{x,n}\),第一位表示数值为n,x为数值为1的数位个数.
  • 所以F中有数值的也只有n个.
  • 然后计算\(F_{x,n}\)时就枚举\(F_{0...x-1}\)和对应的\(G\)乘一下.
  • 然后把非对应位的数值给消掉.
fwt(F[0],len+1,1);
FOR(i,0,n)fwt(G[i],len+1,1);
FOR(i,1,n){
    int *A=F[i];
    FOR(k,0,i-1){
        int *B=F[k],*C=G[i-k];
        FOR(j,0,len)if(B[j]&&C[j])A[j]=(A[j]+(LL)B[j]*C[j])%P;
    }
    fwt(A,len+1,-1);
    FOR(j,0,len)Cnt[j]!=i?A[j]=0:A[j]=(LL)A[j]*Inv[i]%P;
    if(i!=n)fwt(A,len+1,1);
}

分糖果

\[A[0]=Tot-A[1]+A[2]-A[3]+...\]

Description

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

Solution

  • 对于这道题,先考虑链的情况

  • \(Dim\) \(f[i]\)\([1,i]\) 满足条件的方案数,那么由\(f[i-1]\)递推到\(f[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]=\sum \limits _{j

  • 这样就得到了一个\(O(n^2)\)的容斥做法。

  • 然后发现每次的最小值都和当前的i相关,所以可以用单调栈维护一个最小值递增的序列,同时记录贡献就可以得到答案了。

  • 发现这样算出的答案中还是会包含首尾相同的情况。

  • 所以还要继续容斥,为了让分类讨论较为方便,把最小的\(A_i\)放在前面,那么首尾相同时就等于\(f[i-1]\)的方案数,然后再用上面的公式进行转移,即\(f[i]-f[i-1]+f[i-2]-...f[2]\)

Code

#include
#define FOR(i,x,y) for(int i=(x),i##_END=(y);i<=i##_END;++i)
#define DOR(i,x,y) for(int i=(x),i##_END=(y);i>=i##_END;--i)
typedef long long LL;
const int M=1000005;
const int P=1000000007;
inline bool chk_mi(int &x,int y){return x>y?x=y,true:false;}
inline bool chk_mx(int &x,int y){return x=P)x-=P;}
inline void deal(int &x){if(x<0)x+=P;if(x>=P)x-=P;}
inline void Rd(int &res){
    res=0;char c;
    while(c=getchar(),c<48);
    do res=(res<<3)+(res<<1)+(c&15);
    while(c=getchar(),47A[i+1]){
            del(Tot,Sk[top].s);
            --top;
            l=Sk[top].r;
        }
        
        int s=(LL)(Sum[i]-Sum[l])*A[i+1]%P;
        deal(s);
        Sk[++top]=(node){l+1,i,A[i+1],s};
        add(Tot,s);
    }
    
    LL Ans=0;
    FOR(i,2,n){
        if((n-i)&1)Ans-=dp[i];
        else Ans+=dp[i];
    }
    printf("%lld\n",(Ans%P+P)%P);
    
    return 0;
}

转载于:https://www.cnblogs.com/Zerokei/p/9661008.html

你可能感兴趣的:(容斥做题笔记)