五校联考R1 Day2T2 矩阵matrix(容斥)

题目链接

容易想到容斥,但是很恶心,因为要对行和列都容斥,然后行+列又要容斥。。
于是得到(O(nmlog))的做法。
就有70分了:

#include <cstdio>
#include <algorithm>
#define mod (1000000007)
#define Mod(x) (x>mod&&(x-=mod))//>=
#define ID(x,y) ((x-1)*m+y)
typedef long long LL;
const int N=1e5+5,M=1005;

int n,m;

namespace Subtask1
{
	int Cn[N],Cm[N],inv[N];
	inline LL FP(LL x,LL k)
	{
		LL t=1;
		for(; k; k>>=1,x=x*x%mod)
			if(k&1) t=t*x%mod;
		return t;
	}
	LL Calc(LL n,LL m,int *C)
	{
		LL res=0;
		for(int i=1; i<=n; ++i)
			if(i&1) res+=1ll*C[i]*FP(2,(n-i)*m)%mod;
			else res-=1ll*C[i]*FP(2,(n-i)*m)%mod;
		return (res%mod+mod)%mod;
	}
	LL Unique(LL n,LL m)
	{
		LL res=0;
		for(int i=1; i<=n; ++i)
			for(int j=1; j<=m; ++j)
				if((i+j)&1) res+=1ll*Cn[i]*Cm[j]%mod*FP(2,(n-i)*(m-j))%mod;
				else res-=1ll*Cn[i]*Cm[j]%mod*FP(2,(n-i)*(m-j))%mod;
		return (res%mod+mod)%mod;
	}
	void Main(int n,int m)
	{
		Cn[0]=Cm[0]=inv[1]=1;
		for(int i=2,l=std::max(n,m); i<=l; ++i) inv[i]=1ll*(mod-mod/i)*inv[mod%i]%mod;
		for(int i=1; i<=n; ++i) Cn[i]=1ll*Cn[i-1]*(n-i+1)%mod*inv[i]%mod;
		for(int i=1; i<=m; ++i) Cm[i]=1ll*Cm[i-1]*(m-i+1)%mod*inv[i]%mod;
//		printf("%I64d-%I64d-%I64d-%I64d
",FP(2,1ll*n*m),Calc(n,m,Cn),Calc(m,n,Cm),Unique(n,m)-mod);
		printf("%I64d
",((FP(2,1ll*n*m)-(Calc(n,m,Cn)+Calc(m,n,Cm)+Unique(n,m))%mod)%mod+mod)%mod);
	}
}
namespace TEST
{
	int Ans;
	bool col[1005][1005],vis[20000001];
	bool Checkr()//存在某行未染色的方案数 
	{
		for(int i=1; i<=n; ++i)
		{
			for(int j=m; ~j; --j)
				if(!j) return 1;
				else if(col[i][j]) break;
		}
		return 0;
	}
	bool Checkc()//存在某列未染色的方案数 
	{
		for(int j=1; j<=m; ++j)
		{
			for(int i=n; ~i; --i)
				if(!i) return 1;
				else if(col[i][j]) break;
		}
		return 0;
	}
	void DFS(int x,int y,int s)
	{
		if(y>m) y=1, ++x;
		if(x>n)
		{
			if(!vis[s]&&Checkc())
			{
				puts("
OK:");
				for(int i=1; i<=n; ++i,putchar('
'))
					for(int j=1; j<=m; ++j) printf("%d ",col[i][j]);
				++Ans, vis[s]=1;
			}
			return;
		}
		DFS(x,y+1,s), col[x][y]=1, DFS(x,y+1,s|(1<<ID(x,y)-1)), col[x][y]=0;
	}
	void Main()
	{
		DFS(1,1,0), printf("%d
",Ans);
	}
}

int main()
{
	freopen("matrix.in","r",stdin);
	freopen("matrix.out","w",stdout);

	scanf("%d%d",&n,&m);
	if(m==1) return putchar('1'),0;
//	if(n==100000&&m==100000) return printf(""),0;//可能要跑2h。。没早打表
//	TEST::Main();
	if(n<=1000&&m<=1000||1ll*n*m<=1400000||1) {Subtask1::Main(n,m); return 0;}

	return 0;
}

其实只要保证列合法,只对行容斥就可以了。
当确定(k)行不染色时,每列合法的方案数是(2^{n-k}-1),然后(m)列的方案数就是它的(m)次方。

#include <cstdio>
#include <algorithm>
#define mod (1000000007)
typedef long long LL;
const int N=1e5+5;

int inv[N],C[N];

inline LL FP(LL x,LL k)
{
	x<0&&(x+=mod);
	LL t=1;
	for(; k; k>>=1,x=x*x%mod)
		if(k&1) t=t*x%mod;
	return t;
}
LL Calc(LL n,LL m)
{
	LL res=0;
	for(int i=0; i<=n; ++i)
		if(i&1) res-=1ll*C[i]*FP(FP(2,n-i)-1,m)%mod;
		else res+=1ll*C[i]*FP(FP(2,n-i)-1,m)%mod;
	return (res%mod+mod)%mod;
}

int main()
{
	freopen("matrix.in","r",stdin);
	freopen("matrix.out","w",stdout);

	int n,m; scanf("%d%d",&n,&m);
	C[0]=inv[1]=1;
	for(int i=2; i<=n; ++i) inv[i]=1ll*(mod-mod/i)*inv[mod%i]%mod;
	for(int i=1; i<=n; ++i) C[i]=1ll*C[i-1]*(n-i+1)%mod*inv[i]%mod;
	printf("%I64d
",Calc(n,m));

	return 0;
}
原文地址:https://www.cnblogs.com/SovietPower/p/9540065.html