2025 ICPC武汉邀请赛 G [根号分治 容斥原理+DP]

Problem - G - Codeforces

观察题目,我们可以用贡献法, 计算每个格子的贡献,然后累加起来,对于重复的部分我们要减去

1.路径数量

首先,计算两个位置间有多少种路径互通,我们可以利用组合数进行计算:

当两点之间的横坐标距离差的绝对值为x,纵坐标的距离差值为y,那么有方案数ans=c(x+y,x)或c(x+y,y) 二者本身等价

实际意义可以理解为:进行x+y次移动操作,其中x次是横向移动的方案(或者y次是向下移动的方案)

2.计算同种方格的总方案数

对于一种方格,我们记方格中的元素为p;

方案1 :

对于每个方格 如果他的元素为p ,由于一个元素在某条路径上出现多次时只有一次的贡献, 所以该元素第一次出现的所有位置到终点方案数之和就是答案,那么总的方案数就是

我们要算的是:所有「第一次经过数值 p」在点(i,j)的路径数 公式:f(i,j) = 起点到(i,j)总方案数 - Σ( 起点到(u,v)方案数 × (u,v)到(i,j)方案数 )其中(u,v)所有比(i,j)更靠左上 的同值点(u≤i, v≤j(u,v)≠(i,j))。

最终点(i,j)对p的贡献 = f(i,j) × (i,j)到终点的方案数

我们记(x_i,y_i)为该点坐标,(x_j,y_j)为所有同种的方格且(x_j<=x_i&&y_j<=y_i)也就是能转移到(i,j)的所有同种方格;

从起点到该点 且 第一次经过的P元素为该点的 方案数=从起点到(i,j)点的方案数 - c(x_i-x_j+y_i-y_j , x_i -x_j)

该方案数*该点到终点的方案数 = 该点的贡献

时间复杂度为o(k^2),k为该相同元素的数目

方案2 DP:

对于同种元素p

我们n*m的遍历一遍,进行转移 ,记dpij为到达(i,j)的方案数 ,其中如果有和(i,j)相同的元素,我们要视作障碍,这样的话 dpij表达的意思就是不经过与 p 相同的元素走到(i,j)的方案数 。 类似于过河卒问题的DP;

DP的转移就是:dpij= dpi-1j * (ai-1j!=p) + dpij-1*(aij-1!=p)

对于所有为元素P的格子,总的方案数就是: ( dpij*c(n+m-i-j,n-i) ), 其中所有的格子(i,j)都为p元素 , 实际意义也就是:从起点到(i,j) 且 不经过相同P元素的方案数 * 从(i,j)到终点的方案数

时间复杂度为o(nm);

总和考虑两种方案 当相同元素数量k >sqrt( n * m ) 那么方案1的时间复杂度就会高于o(n*m) 此时我们选择方案2

同理当k<sqrt( n * m )的时候我们选择方案1 时间复杂度小于o(n*m);

代码实现:

cpp 复制代码
#include <bits/stdc++.h>
using namespace std;
#define int long long
const int mod=998244353;
const int N=1e5+5,MOD=998244353;
long long fac[N],inv[N];
long long qpow(long long a,long long b,long long mod){
    long long ans=1;
    for(;b;b>>=1){
        if(b&1){
            ans=(1LL*ans*a)%mod;
        }
        a=(1LL*a*a)%mod;
    }
    return ans;
}
void init(int n){
	fac[0]=inv[0]=1;
	for(int i=1;i<=n;i++)fac[i]=(fac[i-1]*i)%MOD;
	inv[n]=qpow(fac[n],MOD-2,MOD);
	for(int i=n-1;i>=1;i--)inv[i]=inv[i+1]*(i+1)%MOD;
}
long long c(int n,int m){
	if(n<m)return 0;
	return fac[n]*inv[m]%MOD*inv[n-m]%MOD;
}
void solve(){
    int n,m;
    cin>>n>>m;
    vector<vector<int>>a(n+2,vector<int>(m+2,0));
    unordered_map<int,vector<pair<int,int>>>mp;
    vector<int>cnt(n*m+2,0);
    int sq=sqrt(n*m);
    for(int i=1;i<=n;i++){
        for(int j=1;j<=m;j++){
            cin>>a[i][j];
            cnt[a[i][j]]++;
            if(cnt[a[i][j]]<=sq)mp[a[i][j]].push_back({i,j});
        }
    }   
    int ans=0;
    vector<bool>vis(n*m+1,0);
    for(int ii=1;ii<=n;ii++){
        for(int jj=1;jj<=m;jj++){
            if(vis[a[ii][jj]])continue;
            vis[a[ii][jj]]=1;
            if(cnt[a[ii][jj]]<=sq){
                auto &v=mp[a[ii][jj]];
                sort(v.begin(),v.end());
                int sz=v.size();
                vector<int>dp(sz+1);
                for(int i=0;i<sz;i++){
                    dp[i]=c(v[i].first+v[i].second-2,v[i].first-1);
                    for(int j=0;j<i;j++){
                        if(v[j].first<=v[i].first&&v[j].second<=v[i].second){
                            dp[i]=(dp[i]-dp[j]*c(v[i].first-v[j].first+v[i].second-v[j].second,v[i].first-v[j].first)%mod+mod)%mod;
                        }
                    }
                }
                int res=0;
                for(int i=0;i<sz;i++){
                    res=(res+dp[i]*c(n+m-v[i].first-v[i].second,n-v[i].first)%mod)%mod;
                }
                ans=(ans+res)%mod;
            }else {
                vector<vector<int>>dp(n+1,vector<int>(m+1,0));
                dp[1][0]=1;
                int res=0;
                for(int i=1;i<=n;i++){
                    for(int j=1;j<=m;j++){
                        dp[i][j]=((dp[i-1][j]*(a[i-1][j]!=a[ii][jj]))+(dp[i][j-1]*(a[i][j-1]!=a[ii][jj])))%mod;
                        if(a[i][j]==a[ii][jj]){
                            res=(res+dp[i][j]*c(n+m-i-j,n-i)%mod)%mod;
                        }
                    }
                }
                ans=(ans+res)%mod;
            }
        }
    }
    cout<<ans<<'\n';
}
signed main() {
    ios::sync_with_stdio(0);
    cin.tie(0);
    cout.tie(0);
    init(N-3);
    int t;
    cin>>t;
    while(t--)solve();    
    return 0;
}
相关推荐
明月_清风39 分钟前
从二叉树到 B+ 树:一文搞懂工程中「树」的演化之道
数据结构·算法·go
渡我白衣42 分钟前
并查集:基础认识与模拟实现
android·java·javascript·数据结构·c++·算法·并查集
hetao17338371 小时前
2026-09-01~09-04 hetao1733837 的刷题记录
c++·算法
Evand J1 小时前
【MATLAB例程,图像滤波5】 反谐波均值滑动窗口滤波(CHMF)图像降噪与质量评价,附代码下载链接
图像处理·算法·计算机视觉·matlab·均值算法·滑动窗口滤波·均值滑动
血小板要健康2 小时前
链表 阶段算法总结
java·数据结构·笔记·算法·leetcode·链表
a187927218312 小时前
【算法】回溯算法(三):三记重锤与 N 皇后——记忆化、状态设计与三层漏斗
算法·leetcode·go·剪枝·回溯·n皇后·算法讲解
HugoStudio_SWAN4 小时前
洛谷 P1420 / P1179 / B4262 最长连号、数字统计与词频统计——统计的三种面孔
c++·学习·程序人生·算法
青 春 记 忆4 小时前
LeetCode 350. 两个数组的交集 II|Python 解法详解
python·算法·leetcode
阳明山水5 小时前
销量预测2025—2026:从模型竞赛到系统融合的范式转型
人工智能·深度学习·算法·机器学习·架构
桐盛科技6 小时前
拒绝“漂绿”风险:基于边缘计算的碳排放数据清洗算法与实时校验逻辑
人工智能·算法·边缘计算