Codeforces Gym 103439D - LIS Counting(猜结论+状压)
一道需要一些猜结论技巧的中档题。
首先突破口在于排列长度恰好等于不是额外输入的某个数 而是 LDS 与 LIS 的乘积,这显然启示我们去找一些性质。根据 dilworth 定理,最长反链等于最小链覆盖,故 LIS 的长度,就是最少需要的递减数列的个数使得每个元素被覆盖至少一次,而每个递减数列长度不超过 ,因此 个递减数列长度不超过 。换句话, 是 的排列长度的最小值。
先考虑怎么计算 的排列个数。大胆猜一个结论:对于符合要求的排列,将其划分为 个递减数列的方案是唯一的。并且如果我们假设 为第 个递减数列中第 个元素在排列中的位置, 为第 个递减数列中第 个元素的值,那么有:
- 。
发现 的安排是两个完全不相干的子问题,因此分别求解方案数然后乘起来就是排列总数。而两者方案数显然是完全相同的,写个阶乘的暴力验证一下也会发现答案确实是完全平方数。因此只用知道怎么计算符合条件的 的个数即可。而这个过程显然相当于每次新填一个数的时候,已经填的数和没填的数的分界线是一条从左下角到右上角,且只能向右或向上的折线。这样的折线最多只有 种,当 时该值大概最多是 级别的,因此直接写个状压 dp 记录折线信息即可。
知道怎么算总数以后,计算 的部分是 trivial 的——枚举这个位置上的值是第 个下降序列的第 个元素,那么对 的限制相当于强制要求 ,预处理 表示强制令 的符合要求的矩阵的方案数,这个可以直接枚举左边部分的折线然后 DP 算一下左右部分的方案数乘起来得到。
const int MAXN=100; const int MAXC=184756; int n,m,mod,rv,f[MAXN+5][MAXN+5][MAXN+5]; int calc(int x,int y,int num){ if(rv)return f[y][x][num]; else return f[x][y][num]; } bool check(vector<int>v){ for(int i=1;i<v.size();i++)if(v[i]>v[i-1])return 0; return 1; } map<vector<int>,int>id; vector<int>cur,v[MAXC+5]; int idcnt=0,dp[MAXC+5]; void dfs(int x,int lst){ if(x==n+1){ reverse(cur.begin(),cur.end()); id[cur]=++idcnt;v[idcnt]=cur; reverse(cur.begin(),cur.end());return; } for(int i=lst;i<=m;i++)cur.pb(i),dfs(x+1,i),cur.ppb(); } int main(){ scanf("%d%d%d",&n,&m,&mod); if(n>m)swap(n,m),rv=1; dfs(1,0);dp[1]=1; for(int i=1;i<=idcnt;i++){ vector<int>cur=v[i]; for(int j=0;j<n;j++)if(cur[j]<m){ cur[j]++; if(check(cur)){ int nid=id[cur]; dp[nid]=(dp[nid]+dp[i])%mod; } cur[j]--; } } for(int i=1;i<=idcnt;i++){ vector<int>cur=v[i]; for(int j=0;j<n;j++)if(cur[j]<m){ cur[j]++; if(check(cur)){ int nid=id[cur],sum=0;for(int k=0;k<n;k++)sum+=cur[k]; vector<int>rst(n); for(int k=0;k<n;k++)rst[k]=m-cur[n-1-k]; f[j+1][cur[j]][sum]=(f[j+1][cur[j]][sum]+1ll*dp[i]*dp[id[rst]])%mod; }cur[j]--; } } if(rv)swap(n,m); for(int i=1;i<=n*m;i++)for(int j=1;j<=n*m;j++){ int res=0; for(int x=1;x<=n;x++)for(int y=1;y<=m;y++) res=(res+1ll*calc(x,y,i)*calc(x,m-y+1,j))%mod; printf("%d%c",res," \n"[j==n*m]); } return 0; }
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 无需6万激活码!GitHub神秘组织3小时极速复刻Manus,手把手教你使用OpenManus搭建本
· Manus爆火,是硬核还是营销?
· 终于写完轮子一部分:tcp代理 了,记录一下
· 别再用vector<bool>了!Google高级工程师:这可能是STL最大的设计失误
· 单元测试从入门到精通
2021-05-01 行列式小记
2021-05-01 Contest 2050 and Codeforces Round #718 (Div. 1 + Div. 2) 题解