bzoj3939 [Usaco2015 Feb]Cow Hopscotch(dp+动态开点线段树)

dp[i][j]表示到i,j的方案数。
我们前缀和优化一下就可以O(1)转移了,复杂度 O(n2)
dp[i][j]=f[i-1][j-1]-颜色为a[i][j]的不合法的方案。
不合法的方案怎么搞呢。
我们对每一种颜色开一棵线段树,维护这种颜色在每一列上的dp值的和。
需要动态开点。
时空复杂度 O(n2logn)

#include 
#include 
#include 
#include 
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;
}

你可能感兴趣的:(bzoj,线段树)