• 容斥做题笔记


    凸轮题

    \[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<i} f[j]*min(A_{j+1...i})*(-1)^{i-j-1}\)

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

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

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

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

    Code

    #include<cstdio>
    #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<y?x=y,true:false;}
    
    int A[M<<1],n;
    int Sum[M],dp[M];
    struct node{int l,r,mi,s;}Sk[M];
    inline void del(int &x,const int &y){x-=y;if(x<0)x+=P;}
    inline void add(int &x,const int &y){x+=y;if(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(),47<c);
    }
    
    int main(){
    	
    	int mx=1e9,id=0;
    	scanf("%d",&n);
    	FOR(i,1,n){
    		Rd(A[i]);
    		if(chk_mi(mx,A[i]))id=i;
    		A[i+n]=A[i];
    	}
    	FOR(i,1,n)A[i]=A[id+i-1];
    	
    	int top=0,Tot=P-A[1];
    
    	FOR(i,1,n){
    		
    		if(i&1)del(dp[i],Tot);
    		else add(dp[i],Tot);
    		
    		Sum[i]=Sum[i-1];
    		if(i&1)add(Sum[i],dp[i]);
    		else del(Sum[i],dp[i]);
    		
    		int l=i-1; 
    		while(top&&Sk[top].mi>A[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;
    }
    
  • 相关阅读:
    SQL学习记录
    Python 函数和变量作用域
    Python 使用socket实现一对多通信
    Flask wtforms validate_on_submit() 无法返回值问题
    Flask WTForm BooleanField用法
    Python3 中的nonlocal用法
    Python 实现二进制循环效果
    Python 各种类型转换
    第一章:数据结构
    Python Challenge
  • 原文地址:https://www.cnblogs.com/Zerokei/p/9661008.html
Copyright © 2020-2023  润新知