dp[i][j]表示到i,j的方案数。
我们前缀和优化一下就可以O(1)转移了,复杂度
O(n2)
。
dp[i][j]=f[i-1][j-1]-颜色为a[i][j]的不合法的方案。
不合法的方案怎么搞呢。
我们对每一种颜色开一棵线段树,维护这种颜色在每一列上的dp值的和。
需要动态开点。
时空复杂度
O(n2logn)
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <vector>
using namespace std;
#define ll long long
#define inf 0x3f3f3f3f
#define N 760
#define mod 1000000007
inline char gc(){
static char buf[1<<16],*S,*T;
if(S==T){T=(S=buf)+fread(buf,1,1<<16,stdin);if(T==S) return EOF;}
return *S++;
}
inline int read(){
int x=0,f=1;char ch=gc();
while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=gc();}
while(ch>='0'&&ch<='9') x=x*10+ch-'0',ch=gc();
return x*f;
}
int n,m,K,owo=0,rt[N*N],dp[N][N],f[N][N],a[N][N];
struct node{
int x,lc,rc;
}tr[N*N*10];
inline void add(int &p,int l,int r,int x,int val){
if(!p) p=++owo;(tr[p].x+=val)%=mod;if(l==r) return;
int mid=l+r>>1;
if(x<=mid) add(tr[p].lc,l,mid,x,val);
else add(tr[p].rc,mid+1,r,x,val);
}
inline int ask(int p,int l,int r,int x,int y){
if(x>y) return 0;if(!p) return 0;
if(x<=l&&r<=y) return tr[p].x;
int mid=l+r>>1,res=0;
if(x<=mid) res+=ask(tr[p].lc,l,mid,x,y);
if(y>mid) res+=ask(tr[p].rc,mid+1,r,x,y);
return res%mod;
}
int main(){
// freopen("a.in","r",stdin);
n=read();m=read();K=read();
for(int i=1;i<=n;++i){
for(int j=1;j<=m;++j){
a[i][j]=read();if(i==1&&j==1){dp[i][j]=f[i][j]=1;continue;}
dp[i][j]=f[i-1][j-1]-ask(rt[a[i][j]],1,m,1,j-1);
dp[i][j]=(dp[i][j]+mod)%mod;
f[i][j]=(f[i-1][j]+f[i][j-1]-f[i-1][j-1])%mod+dp[i][j];
f[i][j]%=mod;if(f[i][j]<0) f[i][j]+=mod;
}for(int j=1;j<=m;++j) add(rt[a[i][j]],1,m,j,dp[i][j]);
}printf("%d\n",dp[n][m]);
return 0;
}