QYU10742 [ICPC-2025 Inv WHN] 路径求和问题 学习笔记

QYU10742 [ICPC-2025 Inv WHN] 路径求和问题 学习笔记

QingyuOJ Link

题意简述

有一个 \(n\times m\) 的网格。坐标为 \((i,j)\) 的格子有一个颜色 \(c_{i,j}\)。定义一条路径的权值为经过格子的颜色种数。求所有从 \((1,1)\) 开始,只往右或往下走,最后走到 \((n,m)\) 的路径的权值总和。

\(n\times m\le 10^5\)

做法解析

我们显然一种颜色一种颜色的分析。每种颜色 \(i\) 造成的贡献等于:所有经过颜色 \(i\) 格子且满足题意的路径的数量。

这个问题有两种做法。

  • 一种是 \(O(nm)\) 的。不需要我教你这个形如 \(dp_{i,j}=dp_{i-1,j}\otimes dp_{i,j-1}\) 的东西怎么做吧。
  • 一种是 \(O(k^2)\) 的。其中 \(k\) 为该颜色格子的数量。这与 CFP559C 的做法同理(仅在最后一步有略微不同)。

然后你拿根号分治把它们拼起来就行了。

代码实现

#include <bits/stdc++.h>
using namespace std;
using namespace obasic;
using namespace omodint;
using mint=m998;
const int MaxN=1e5+5,MaxNr=320,MaxK=2e3+5;
mint facr[MaxN*2],finv[MaxN*2];
using namespace omathe;
int N,M,X,Y,cnt[MaxN],klim;
vector<int> C[MaxN];
vector<array<mint,2>> dp1[MaxN];
mint dp2[MaxNr];
struct anob{
    int x,y;
    friend bool operator<(anob a,anob b){return a.x==b.x?a.y<b.y:a.x<b.x;}
};
vector<anob> A[MaxN];
mint solve1(int cc){
    dp1[1][0][0]=1;
    for(int i=1;i<=N;i++){
        for(int j=1;j<=M;j++){
            dp1[i][j][1]=dp1[i-1][j][1]+dp1[i][j-1][1];
            mint tmp=dp1[i-1][j][0]+dp1[i][j-1][0];
            if(C[i][j]==cc)dp1[i][j][1]+=tmp,dp1[i][j][0]=0;
            else dp1[i][j][0]=tmp;
        }
    }
    return dp1[N][M][1];
}
mint solve2(int u){
    mint res=0;
    sort(A[u].begin(),A[u].end());
    for(int i=0;i<cnt[u];i++){
        auto [xi,yi]=A[u][i];
        dp2[i]=Comb(xi+yi-2,xi-1);
        for(int j=0;j<i;j++){
            if(A[u][j].y>A[u][i].y)continue;
            auto [xj,yj]=A[u][j];
            dp2[i]-=dp2[j]*Comb((xi-xj)+(yi-yj),(xi-xj));
        }
        res+=dp2[i]*Comb((N-xi)+(M-yi),(N-xi));
    }
    return res;
}
mint ans;
void befinit(int n,int m){
    klim=sqrt(n*m);
    for(int i=0;i<=n;i++){
        C[i].resize(m+1);
        dp1[i].resize(m+1);
    }
    for(int i=1;i<=n*m;i++)A[i].clear();
    ans=0,fill(cnt,cnt+n*m+1,0);
}
void mian(){
    readis(N,M),befinit(N,M);
    for(int i=1;i<=N;i++){
        for(int j=1;j<=M;j++){
            readi(X),C[i][j]=X;
            cnt[X]++,A[X].push_back({i,j});
        }
    }
    for(int i=1;i<=N*M;i++)if(cnt[i])ans+=(cnt[i]>klim?solve1(i):solve2(i));
    writil(miti(ans));
}
int Tcn;
int main(){
    readi(Tcn),premwork(Ocp5*2);
    while(Tcn--)mian();
    return 0;
}
posted @ 2025-04-29 00:01  矞龙OrinLoong  阅读(38)  评论(0)    收藏  举报