CF997C Sky Full of Stars 数论

这篇博客介绍了CF997C问题的两种解法,主要利用容斥原理进行计算。第一种方法通过优化暴力求解的复杂度,将问题转化为求解特定组合数的乘积和指数形式。第二种方法则是通过转换问题,将求解至少有i行j列相同的方案数转化为总数减去每一行每一列都不同的方案数。博主详细阐述了两种方法的思路和公式推导过程,并指出代码实现并不复杂。
摘要由CSDN通过智能技术生成

正解:容斥

解题报告:

传送门!

两个方法,分别港下QAQ

先说第一种

首先要推出式子,就∑2*C(i,n)*(-1)i+1*3i*3n*n-n+3*∑∑(-1)i+j+1*C(i,n)*C(j,n)*3(n-i)(n-j)

理解其实不难理解的?就至少有i行j列相同的方案数嘛

具体来说,首先从n行中选i行,n列中选j列的方案数是C(i,n)*C(j,n),然后除了这i行这j列以外还有(n-i)(n-j)个格子,这些格子都可以随便填的,所以是3(n-i)(n-j),然后选出的这i行j列的颜色有3种可能所以最前面有个3,容斥不解释了太显然

然后这显然是个暴力,要n2地跑,过不去

所以考虑怎么优化QAQ

可以发现前面那个复杂度是对的麻油什么问题,是后面那个式子要优化嘛,所以只看后面那个式子就好

可以先把i提到前面,就变成了3*∑(-1)i+1*C(i,n)∑(-1)j*C(j,n)*3(n-i)(n-j)

然后这样子,有点麻烦,可以考虑把i替换成n-i,j替换成n-j,又因为C(n-i,n)=C(i,n),C(n-j,n)=C(j,n)

所以就变成,3*∑(-1)i+1*C(i,n)∑(-1)i+j+1*C(j,n)*3i*j,-1放进去就是3*∑(-1)i+1*C(i,n)∑*C(j,n)*(-3i)*j

然后考虑二项式定理,(a+b)n=∑C(i,n)*ai*bn-i

所以后面∑(-1)j*C(n-j,n)*3(n-i)(n-j)这一堆,就可以变成(1-3i)n

但是这里注意一下,二项式定理中的i是从0开始的,然后上面列出来的式子是从1开始的,所以加多了,所以要减一个[(-3)i]n

综上,就求个∑2*C(i,n)*(-1)i+1*3i+n*n-n+3*∑(-1)i+1*C(i,n)*[(1-3i)n-(-3i)n]

大概就这样儿,over

#include<bits/stdc++.h>
using namespace std;
#define il inline
#define ll long long
#define gc getchar()
#define rl register ll
#define rc register char
#define rb register bool
#define rp(i,x,y) for(rl i=x;i<=y;++i)
#define my(i,x,y) for(rl i=x;i>=y;--i)

const ll N=1000000+10,mod=998244353;
ll n,c[N],as;

il ll read()
{
    rc ch=gc;rl x=0;rb y=1;
    while(ch!='-' && (ch>'9' || ch<'0'))ch=gc;
    if(ch=='-')ch=gc,y=0;
    while(ch>='0' && ch<='9')x=(x<<1)+(x<<3)+(ch^'0'),ch=gc;
    return y?x:-x;
}
il ll power(rl x,rl y){rl as=1;while(y){if(y&1)as=1ll*(as*x)%mod;x=1ll*(x*x)%mod;y>>=1;}return as;}
il void pre(){c[0]=1;rp(i,1,n)c[i]=c[i-1]*(n-i+1)%mod*power(i,mod-2)%mod;}

int main()
{
    freopen("cf997c.out","w",stdout);
    n=read();if(n==1)return printf("3\n"),0;pre();
    rp(i,1,n)as=(as+c[i]*(i&1?1:mod-1)%mod*power(3,i+n*n-n*i)%mod)%mod;as=(as<<1)%mod;
    rp(i,1,n){rl powwer=power(3,n-i);as=(as+3*c[i]%mod*(i&1?1:mod-1)%mod*(power(powwer-1,n)-power(powwer,n)+mod)%mod)%mod;}
    printf("%lld\n",as);
    return 0;
}
这儿是代码QAQ

然后港下法二

首先看上面那个,是正着推的嘛

所以考虑转化一下,变成总数-每一行每一列都不同的方案数,也就是-每一行每一列至少有2种颜色的方案数

然后这里是有两个限制嘛,一个行一个列

这里就有一个套路,就是说如果有两个限制,就把一个容斥了,另一个就能比较简单地做掉

就首先想列,首先对某一列的总共染色方案是3n,然后不合法的方案有3种,就3n-3,然后有n列,所以总的是(3n-3)n

然后考虑这样子显然还会多减去合法的行相等的情况

然后多减的是什么的,就至少有一行颜色相同且每一列至少有两种颜色的方案

所以就枚举有多少行是只有一种颜色的

一样先放式子再解释

首先C(i,n)不解释,你肯定要从n行中选出i行来嘛

然后这时候会发现其实是有两种情况的

一种是这些颜色相同的行的颜色都相同,那就只有3种颜色可以选择

然后就还会剩下n*(n-i)个格子麻油涂颜色嘛

这时候想,因为我们是在任何一列至少有两种颜色的大背景下考虑的,所以如果我们现在减了一个某一列只有一种颜色的方案就是减多了对趴

所以对这些格子n列分别考虑,对每一列,本来应该有3n-i种选择方案,然后发现如果所有格子都选了和最上面那i行颜色相同的方案就GG了,所以-1

所以这里就是3*(3n-i-1)n

还一种就他们颜色不是全部相同咯

那对这i行就有3i种方案,因为上面我们已经算过所有行都相同颜色的情况了嘛,所以要减三种,所以是3i-3

然后这时候其他格子就可以放心大胆涂辣显然不会有问题,所以3n*(n-i)

综上,这一段的式子就是C(i,n)*(3*(3n-i-1)n+(3i-3)*3n*(n-i))

最后总结一下,整个题目的式子就可以表示成3n*n-(3n-3)n-(-1)i*C(i,n)*(3*(3n-i-1)n+(3i-3)*3n*(n-i))

代码难度并不大,,,所以就懒得打了QAQ放个别人的代码QAQ

 

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<vector>
using namespace std;
#define MOD 998244353
#define MAX 1000100
int fpow(int a,int b){int s=1;while(b){if(b&1)s=1ll*s*a%MOD;a=1ll*a*a%MOD;b>>=1;}return s;}
int n;
int jc[MAX],jv[MAX],inv[MAX];
int C(int n,int m){return 1ll*jc[n]*jv[m]%MOD*jv[n-m]%MOD;}
int p[MAX];
int main()
{
    freopen("A.in","r",stdin);
    freopen("A.out","w",stdout);
    cin>>n;if(n==1){puts("3");return 0;}
    jc[0]=jv[0]=inv[0]=inv[1]=1;
    for(int i=2;i<=n;++i)inv[i]=1ll*inv[MOD%i]*(MOD-MOD/i)%MOD;
    for(int i=1;i<=n;++i)jc[i]=1ll*jc[i-1]*i%MOD;
    for(int i=1;i<=n;++i)jv[i]=1ll*jv[i-1]*inv[i]%MOD;
    p[0]=fpow(fpow(3,n)-3,n)%MOD;
    for(int j=1;j<=n;++j)
    {
         int s=1ll*(fpow(3,j)-3)*fpow(fpow(3,n-j),n)%MOD;
        int QwQ=fpow(fpow(3,n-j)-1,n);
         s=(s+3ll*QwQ)%MOD;
        p[j]=1ll*C(n,j)*s%MOD;
    }
    int ans=p[0];
    for(int i=1,d=MOD-1;i<=n;++i,d=MOD-d)ans=(ans+1ll*d*p[i])%MOD;
    ans=(fpow(fpow(3,n),n)+MOD-ans)%MOD;
    printf("%d\n",ans);
    return 0;
}
View Code

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值