P3120 [USACO15FEB]Cow Hopscotch G 題解
阿新 • • 發佈:2021-10-28
題目連結
P3120 [USACO15FEB]Cow Hopscotch G
solve
讀完題目我們很容易寫出一個 \(O(n^4)\) 的方程
\[F[i][j]=\sum F[i'][j'] \]這個柿子可以通過三維偏序來優化,但是我往另外一個方向走了
如果顏色數很少的話,就可以用一個數組 \(sum[i][j][k]\) 表示第 \(k\) 個顏色在 \(i,j\) 左上角的方案書之和,\(sum[i][j][0]\)表示總的,所以方程就變成了
\[F[i][j]=sum[i-1][j-1][0]-sum[i-1][j-1][a[i][j]] \]但是這裡的顏色數達到了 \(n\times m\)
我們每行每行處理,先求後改,開 \(k\) (顏色數) 顆線段樹,於是方案就變成了
\[sum[j-1]-\text{第a[i][j]的1到{j-1}的字首和} \]每次求完回去再同意修改即可
程式碼實現
#include<bits/stdc++.h> using namespace std; typedef long long LL; const int maxn=755; const LL TT=1e9+7; int N,M,cnt; int a[maxn][maxn]; LL F[maxn][maxn],S[maxn]; inline int read(){ int ret=0,f=1;char ch=getchar(); while(ch<'0'||ch>'9'){if(ch=='-')f=-f;ch=getchar();} while(ch<='9'&&ch>='0')ret=ret*10+ch-'0',ch=getchar(); return ret*f; } struct Tree{ LL sum; int ls,rs; }tr[maxn*maxn*16]; inline void update(int x){ tr[x].sum=(tr[tr[x].ls].sum+tr[tr[x].rs].sum)%TT; } void modify(int &x,int l,int r,int pos,int data){ if(!x)x=++cnt; if(!(l^r)){tr[x].sum=(tr[x].sum+data)%TT;return ;} int mid=(r-l>>1)+l; (pos<=mid)&&(modify(tr[x].ls,l,mid,pos,data),0);(pos>mid)&&(modify(tr[x].rs,mid+1,r,pos,data),0); update(x); return ; } LL query(int x,int l,int r,int L,int R){ if(!x)return 0; if(L<=l&&r<=R)return tr[x].sum; int mid=(r-l>>1)+l; LL ret=0; (L<=mid)&&(ret=(ret+query(tr[x].ls,l,mid,L,R))%TT,0);(R>mid)&&(ret=(ret+query(tr[x].rs,mid+1,r,L,R))%TT,0); return ret; } int main(){ freopen("P3120.in","r",stdin); freopen("P3120.out","w",stdout); N=read();M=read();cnt=read(); for(int i=1;i<=N;i++) for(int j=1;j<=M;j++) a[i][j]=read(); for(int i=1;i<=M;i++) S[i]=1;F[1][1]=1;modify(a[1][1],1,M,1,1); for(int i=2;i<=N;i++){ for(int j=2;j<=M;j++)F[i][j]=(S[j-1]-query(a[i][j],1,M,1,j-1)+TT)%TT; LL res=0; for(int j=2;j<=M;j++){ res=(res+F[i][j]); S[j]=(S[j]+res)%TT; modify(a[i][j],1,M,j,F[i][j]); } } printf("%lld\n",F[N][M]); return 0; }