AGC061 F Perfect String
毒瘤出题人,史诗加强 AGC 的 F……
然而我连原题都不会,所以只学了原题做法。
翻译一下题意就是给定一张循环网格图,求经过 \((0,0)\) 的闭合回路条数。
由于网格图中每一个位置都等价,所以转化成求所有闭合回路长度之和。
考虑回路的形态,由于必须要走到右边界/下边界才可以回到起点,所以相当于是从左边界/上边界的若干个插头走到右边界/下边界的若干个插头且路径不交的方案数。
这是经典的 \(\text{LGV Lemma}\) 的形式。显然插头只会按照顺序匹配,否则一定有交叉。我们对相对的行和相对的列的插头编上同样的编号(这样子做的原因以下会说),那么 \((-1)^{\mathrm{inv}(p)}\) 始终是 \((-1)^{ij}\)。
有些插头的放置方案会导致连出的不止一个环,然而由于插头总是按顺序匹配,所以连出一个环当且仅当 \(\gcd(r,c)=1\),其中 \(r,c\) 是放插头的行数和列数。
直接做对于每一个行的子集和列的子集求答案,指数级显然不现实。
然而一组子集的贡献只与 \(r,c\) 有关,所以我们考虑计算一个改造一下原 \(\text{DAG}\)。

对每一个插头新建原汇点,然后加入额外边,如果走新加入的额外边代表不选这个行/列,否则乘上一个占位符 \(x/y\)。
加入额外边逆序对数看起来会变,然而由于我们相对的插头编号一样,走额外边相当于加入自环,排列的奇偶性又只跟环的个数及环长有关,所以加入一些自环永远不会改变排列的奇偶性。
接下来总不至于带着占位符求行列式吧?二元多项式相乘还要求逆想想就可怕。
然而我们可以求出这个二元多项式 \((n+1)\times (m+1)\) 个点值进行二元多项式插值。
具体地,我们仿照一元拉格朗日插值构造二元多项式 \(F\):
\]
其中 \(z_{i,j}=F(x_i,x_j)\)。
具体怎么计算这个式子呢?
我们考虑拉格朗日插值类似 \(\text{IDFT}\) 是一个线性变换,相当于乘上了一个范德蒙德矩阵的逆矩阵。
类似二元 \(\text{DFT}\) 或者 \(\text{FWT}\) 处理高维多项式的思想,各维之间独立,那么我们先对一维进行线性变换,然后再对进行线性变换后的结果对另一维执行线性变换。
就比如说这里 \(z_{i,j}\),我们先把 \(z\) 看成 \(n+1\) 个行向量,对这个行向量进行拉格朗日插值,把插值得到的系数塞回原来的位置,然后把 \(z\) 竖着的列看成列向量,再拉格朗日插值一遍塞回原来的位置。此时 \(z\) 就变成了二元多项式的系数表示。
这启发我们 \(\text{FWT}\) 的核心思想就是线性变换关于各维独立,所以逐维进行线性变换就是对的,对一维做完后把结果塞回原来的位置再做另一维。
还有不清楚的可以直接推式子:
\]
发现括号内的东西就是一元拉插的式子,设进行插值后的结果是 \(G_i(y)=\sum_{j=0}^m g_{i,j} y^j\)。
=\sum_{j=0}^m y^j (\sum_{i=0}^n g_{i,j} \prod_{i'\neq i} \frac{x-x_{i'}}{x_i-x_{i'}})
\]
括号又是一元拉插的式子,再插一下就做完了。
#include <cstdio>
#include <algorithm>
using namespace std;
int read(){
char c=getchar();int x=0;
while(c<48||c>57) c=getchar();
do x=(x<<1)+(x<<3)+(c^48),c=getchar();
while(c>=48&&c<=57);
return x;
}
const int P=998244353;
const int N=103,Lim=1003;
typedef long long ll;
int n,m;
int fac[Lim],fiv[Lim],inv[Lim];
int qp(int a,int b=P-2){
int res=1;
while(b){
if(b&1) res=(ll)res*a%P;
a=(ll)a*a%P;b>>=1;
}
return res;
}
int comb(int a,int b){
return (ll)fac[a+b]*fiv[a]%P*fiv[b]%P;
}
void inc(int &x,int v){if((x+=v)>=P) x-=P;}
void dec(int &x,int v){if((x-=v)<0) x+=P;}
int a[N][N];
int deter(int len){
bool sgn=0;
for(int i=1;i<=len;++i){
int p=i;
while(p<=len&&!a[p][i]) ++p;
if(p!=i){
for(int j=i;j<=len;++j) swap(a[i][j],a[p][j]);
sgn=!sgn;
}
int iv=qp(a[i][i]);
for(int j=i+1;j<=len;++j){
int tmp=(ll)iv*a[j][i]%P;
for(int k=i;k<=len;++k)
dec(a[j][k],(ll)tmp*a[i][k]%P);
}
}
int res=1;
if(sgn) res=P-1;
for(int i=1;i<=len;++i) res=(ll)res*a[i][i]%P;
for(int i=1;i<=len;++i)
for(int j=1;j<=len;++j) a[i][j]=0;
return res;
}
int solve(int x,int y){
for(int i=1;i<=n;++i){
for(int j=1;j<=m;++j)
a[i][j+n]=(ll)comb(n-i,j-1)*x%P;
for(int j=i;j<=n;++j)
a[i][j]=((ll)comb(j-i,m-1)*x+(i==j))%P;
}
for(int i=1;i<=m;++i){
for(int j=1;j<=n;++j)
a[i+n][j]=(ll)comb(j-1,m-i)*y%P;
for(int j=i;j<=m;++j)
a[i+n][j+n]=((ll)comb(n-1,j-i)*y+(i==j))%P;
}
return deter(n+m);
}
void init(int len){
inv[1]=1;
for(int i=2;i<=len;++i) inv[i]=(ll)inv[P%i]*(P-P/i)%P;
fac[0]=1;
for(int i=1;i<=len;++i) fac[i]=(ll)fac[i-1]*i%P;
fiv[len]=qp(fac[len]);
for(int i=len;i;--i) fiv[i-1]=(ll)fiv[i]*i%P;
}
int b[N][N];
int c[N],d[N],e[N],sz;
void mul(int v,int s){
for(int i=++sz;i;--i){
d[i]=(ll)d[i]*v%P;
inc(d[i],d[i-1]);
}
d[0]=(ll)d[0]*v%P;
for(int i=0;i<=sz;++i) d[i]=(ll)d[i]*s%P;
}
void lagrange(int len){
for(int i=0;i<=len;++i) e[i]=0;
for(int i=0;i<=len;++i){
d[sz=0]=c[i];
for(int j=0;j<i;++j) mul(P-j,inv[i-j]);
for(int j=len;j>i;--j) mul(P-j,P-inv[j-i]);
for(int j=0;j<=len;++j) inc(e[j],d[j]);
while(sz) d[sz--]=0;
}
}
int gcd(int a,int b){
if(!b) return a;
return gcd(b,a%b);
}
int main(){
init(1000);
n=read();m=read();
if(n==1||m==1){puts("2");return 0;}
if(n==2){printf("%d\n",(qp(2,m-1)+qp(2,m-2)+1)%P);return 0;}
if(m==2){printf("%d\n",(qp(2,n-1)+qp(2,n-2)+1)%P);return 0;}
for(int i=0;i<=n;++i){
for(int j=0;j<=m;++j) c[j]=solve(i,j);
lagrange(m);
for(int j=0;j<=m;++j) b[i][j]=e[j];
}
for(int j=0;j<=m;++j){
for(int i=0;i<=n;++i) c[i]=b[i][j];
lagrange(n);
for(int i=0;i<=n;++i) b[i][j]=e[i];
}
int res=0;
for(int i=0;i<=n;++i)
for(int j=0;j<=m;++j){
if(!i&&!j) continue;
if(gcd(i,j)==1)
if(i&j&1) dec(res,(ll)(i*m+j*n)*b[i][j]%P);
else inc(res,(ll)(i*m+j*n)*b[i][j]%P);
}
res=(ll)res*qp(n*m)%P;
printf("%d\n",res);
return 0;
}
拉插实现的比较丑,只写了 \(O(n^4)\),可以做到更优复杂度但没必要。
AGC061 F Perfect String的更多相关文章
- Codeforces Round #582 (Div. 3) F. Unstable String Sort
传送门 题意: 你需要输出一个长度为n的字符序列(由小写字母组成),且这个字符串中至少包含k个不同的字符.另外题目还有要求:给你两个长度为p和q的序列,设字符序列存在s中 那么就会有s[Pi]< ...
- CF - 1117 F Crisp String
题目传送门 题解: 枚举非法对. 如果 ‘a' 和 ’b' 不能相邻的话,那么删除 'a' 'b'之间的字符就是非法操作了. 假设题目给定的字符串为 "acdbe",所以删除cd ...
- String字符串的完美度
题目详情: 我们要给每个字母配一个1-26之间的整数,具体怎么分配由你决定,但不同字母的完美度不同, 而一个字符串的完美度等于它里面所有字母的完美度之和,且不在乎字母大小写,也就是说字母F和f的完美度 ...
- 关于string.format() 转
string.format()函数用来生成具有特定格式的字符串,这个函数有两个参数,第一个参数为格式化串:由指示符和控制格式的字符组成.第二个参数是对应格式中每个代号的各种数据. 格式字符串可能包含以 ...
- 【C#】 格式化说明符 string.Format WriteLine
定义 格式说明符的语法由3个字段组成:索引号.对齐说明符和格式字段.String.Format和WriteLine都遵守同样的格式化规则. 对齐说明符 对齐说明符表示了字段中字符的最小宽度.对齐说明符 ...
- String、StringBuffer、StringBuilder的一些小经验……
一说String.StringBuffer和StringBuilder,想必大家都很熟悉,这三者经常在我们的面试题中出现,我也是看到了关于这三个的经典面试题,才触动了我之前工作中的一些经历,故而根据我 ...
- java中遇到过的String的一些特性
1.string对象是final的? String str="asdfdf"; str.replace("as",""); System.o ...
- C++中int,float,string,char*的转换(待续)
//float转string char a[100]; float b = 1.234; sprintf(a, "%f", b); string result(a); //int转 ...
- Java中的Scanner类和String类
1:Scanner的使用(了解) (1)在JDK5以后出现的用于键盘录入数据的类. (2)构造方法: A:讲解了System.in这个东西. 它其实是标准的输入流,对应于键 ...
- 【Java】Java创建String时,什么情况放进String Pool?
对Java创建String是否放入String pool作代码性的试验. 参考的优秀文章 JAVA面试题解惑系列(二)——到底创建了几个String对象? public String(String o ...
随机推荐
- 2.3Dmax界面_视图调整
一.试图模型显示效果的切换 '默认是真实显示效果' 线框模式 快捷键F3 ----> 真实显示效果和线框显示效果的切换(切换到线框显示效果再按F3就切换到了真实显示效果). 线面模式 快捷键F4 ...
- button 样式 触发器
<Style x:Key="Style.OkOperationButton" TargetType="ButtonBase"> <Setter ...
- SDK测试标准
测试分类 具体测试项 测试内容 测试方法 文档测试 接口清单 接口清单是否完整,正确,包含提供给开发者的协议所有字段的定义和解释 人工检查 更新说明 要说明新增,删除的接口定义 Demo示例 显示如何 ...
- [ACTF2020 新生赛]Include 1
首先进入靶场可以看到trip 查看源码 点击进入提示我们能不能找到flag 可以看到这里是文件包含,想着包含index.php但是根目录是自动索引的,无论输入什么都是trip页面 又想着包含flag. ...
- c++ 内存顺序
搞懂无锁编程的重要一步是完全理解内存顺序! 本教程由作者和ChatGPT通力合作完成. 都有哪几种? c++的内存模型共有6种 memory_order_relaxed memory_order_co ...
- Nacos的微服务与本地测试的问题
前提条件: 这里是微服务上的yml的配置: uri: base-svc-authcenter: 192.168.1.121:28002 base-svc-file: 192.168.1.121:280 ...
- 思必驰周强:AI 和传统信号技术在实时音频通话中的应用
如何用 AI 解决声音传输&处理中的三大问题?三大问题又是哪三大问题? 在「RTE2022 实时互联网大会」中,思必驰研发总监 @周强以<AI 和传统信号技术在实时音频通话中的应用> ...
- 发布新版博客备份功能:生成 sqlite 数据库文件,vscode 插件可查看
大家好,最近我们重新开发了园子的博客备份功能,今天发布第一个预览版,欢迎大家试用. 点击博客后台侧边栏的博客备份进入新版博客备份: 点击创建备份按钮创建博客备份任务(目前每天只能创建一次备份),待备份 ...
- Oracle 服务器概念梳理
Oracle 公司是世界上最大的信息管理软件及服务提供商,因其复杂的关系数据库产品而闻名.Oracle 的关系数据库是世界上第一个支持 SQL 语言的数据库.支持服务器/客户机等部署.Oracle 数 ...
- flutter---->flutter orientation
Get the orientation 1. Media Query import 'package:flutter/material.dart'; void main() => runApp( ...