Codeforces997C Sky Full of Stars 【FMT】【组合数】

题目大意:

一个$n*n$的格子,每个格子由你填色,有三种允许填色的方法,问有一行或者一列相同的方案数。

题目分析:

标题的FMT是我吓人用的。

一行或一列的问题不好解决,转成它的反面,没有一行和一列相同的方案数。

从一个方向入手,比如列,把一列看成一个整体。把颜色看成二进制数,$001$,$010$,$100$。

那么一列构成了一个长度为$3n$的二进制数,$n$列之间互相与出来的结果为$0$。实际我要统计这个东西。

注意到每一列的取法是不能取相同颜色的,所以剔除相同。之后我们得到了每一列可选的情况。

将它做FMT,之后做$n$次方,然后做IFMT,$0$位上的就是答案。用$9^n$减去这个数字就行。

直接做的时间复杂度是$O(n*2^n)$的,我们远不能承受。

但是我们有用的状态却不多,甚至还有规律。比如FMT后的某个位$bit$如果每三位出现两个$1$那么这个的FMT值一定是$0$,然后如果每三位只有$1$个$1$那么该位贡献$1$次,否则贡献$3$次。

然后是IFMT的还原问题,经过观察,不难发现如果某个位$bit$的$1$的个数为奇数,那么对$0$位产生减的影响,否则产生加的影响。

综合上面两个因素,可以利用组合数来统计方案数。值得注意的是如果每三位的1的位置相同那么要提防填充出相同结果。

时间复杂度$O(n)$

代码:

1 1 #include<bits/stdc++.h> 2 2 using namespace std; 3 3 4 4 const int maxn = 1020000; 5 5 const int mod = 998244353; 6 6 7 7 int n; 8 8 9 9 int f[maxn][2],g[maxn][2]; 1010 int pw3[maxn]; 1111 int c[maxn]; 1212 1313 int fast_pow(int now,int pw){ 1414 int ans = 1,dt = now,bit = 1; 1515 while(bit <= pw){ 1616 if(bit & pw) ans = (1ll*ans*dt)%mod; 1717 bit <<=1;dt = (1ll*dt*dt)%mod; 1818 } 1919 return ans; 2020 } 2121 2222 void work(){ 2323 if(n == 1) {puts("3");return;} 2424 pw3[0] = 1;for(int i=1;i<=n;i++) pw3[i] = 3ll*pw3[i-1]%mod; 2525 c[0] = 1; 2626 for(int i=1;i<=n;i++){ 2727 c[i]=(1ll*c[i-1]*(n-i+1))%mod; 2828 c[i]=(1ll*c[i]*fast_pow(i,mod-2))%mod; 2929 } 3030 int sum = fast_pow(pw3[n],n); 3131 f[0][1] = 1; g[0][1] = (pw3[n]-3+mod)%mod; 3232 f[n][0] = (pw3[n]-3+mod)%mod; g[n][0] = 1; 3333 for(int i=1;i<n;i++){ 3434 f[i][1] = 3ll*c[i]%mod; 3535 f[i][0] = (1ll*pw3[i]*c[i])%mod; 3636 f[i][0] -= f[i][1]; f[i][0] += mod; f[i][0] %= mod; 3737 g[i][0] = pw3[n-i]; g[i][1] = (pw3[n-i]-1+mod)%mod; 3838 } 3939 for(int i=0;i<=n;i++){ 4040 g[i][0] = fast_pow(g[i][0],n); g[i][1] = fast_pow(g[i][1],n); 4141 int dr = ((i&1)?1:-1); 4242 sum += dr*(1ll*g[i][0]*f[i][0])%mod; 4343 if(sum >= mod) sum-=mod; if(sum < 0) sum += mod; 4444 sum += dr*(1ll*g[i][1]*f[i][1])%mod; 4545 if(sum >= mod) sum-=mod; if(sum < 0) sum += mod; 4646 } 4747 printf("%d",sum); 4848 4949 } 5050 5151 int main(){ 5252 scanf("%d",&n); 5353 work(); 5454 return 0; 5555 }
点赞
收藏

评论区

加载中...

相关推荐

MySQL查询结果集字符串操作之多行合并与单行分割

前言我们在做项目写sql语句的时候,是否会遇到这样的场景,就是需要把查询出来的多列,按照字符串分割合并成一列显示,或者把存在数据库里面用逗号分隔的一列,查询分成多列呢,常见场景有,文章标签,需要吧查询多个标签合并成一列,等,需要怎么去实现呢,这就涉及到MySQL的字符串操作groupconcat场景再现我想把查询多列数据合并成一列显示用逗号分隔

python刷题-数列特征

问题描述给出n个数,找出这n个数的最大值,最小值,和。输入格式第一行为整数n,表示数的个数。第二行有n个数,为给定的n个数,每个数的绝对值都小于10000。输出格式输出三行,每行一个整数。第一行表示这些数中的最大值,第二行表示这些数中的最小值,第三行表示这些数的和。样例输入513245样例输出5211数据规模与约定1<n<10000。

Spark DataFrame列的合并与拆分

版本说明:Spark2.3.0使用SparkSQL在对数据进行处理的过程中,可能会遇到对一列数据拆分为多列,或者把多列数据合并为一列。这里记录一下目前想到的对DataFrame列数据进行合并和拆分的几种方法。1DataFrame列数据的合并例如:我们有如下数据,想要将三列数据合并为一列,并以“,”分割

LeetCode

一目录不折腾的前端,和咸鱼有什么区别目录一目录二题目三解题思路四统计分析五解题套路二题目在一个nm的二维数组中:每一行都按照从左到右递增的顺序排序,每一列都按照从上到下递增的顺序排序。请完成一个函数,输入这样的一个二维数组和一个整数,判断数组中是否含有该整数。示例

Apache POI 合并单元格

合并单元格所使用的方法:sheet.addMergedRegion(CellRangeAddress  cellRangeAddress  );CellRangeAddress  对象的构造方法需要传入合并单元格的首行、最后一行、首列、最后一列。CellRangeAddresscranewCellRangeAddress(0,3,3,

使用伸缩盒布局创建一个三列布局每一列占用`col

\使用伸缩盒布局创建一个三列布局每一列占用\col{n}\/12份基于父级容器的宽度\Answer设置\.row\类的父级容器为\display:flex;\样式然后使用\flex\缩写形式属性给每一列子元素设置一个\flexgrow\值使得每一列可以按照设置的比例自动协调宽度

Codeforces997C Sky Full of Stars 【FMT】【组合数】 - HelloWorld