您的位置:首页 > Web前端

BZOJ3939: [Usaco2015 Feb]Cow Hopscotch

2016-08-11 16:18 302 查看
题目大意:给定一个棋盘,从左上角走到左下角,每次必须严格向右下走一步并且两个各自的标号不能相同,问一共有多少种走法

设F[i][j]表示走到(i,j)这个格子的方案数,那么转移方程就是严格左上的所有格子的F值和减去和他相同颜色的F值和,前者可以直接维护前缀和得出,后者可以对每种颜色建一个动态开点的线段树来维护,时间复杂度O(NMlognm),空间复杂度O(NMlognm)

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#define N 751
#define M 6000010
int mod=1e9+7;
using namespace std;
int f

,a

,pre

;
int rt[N*N],ch[M][2],sum[M],cnt;
void addnew(int &y,int l,int r,int v,int w)
{
if(!y) cnt++,y=cnt;
sum[y]=(sum[y]+w)%mod;
if(l==r) return;
int mid=(l+r)>>1;
if(v<=mid) addnew(ch[y][0],l,mid,v,w);
else addnew(ch[y][1],mid+1,r,v,w);
}
int check(int x,int l,int r,int ll,int rr)
{
if(l==ll&&r==rr) return sum[x];
int mid=(l+r)>>1;
if(rr<=mid) return check(ch[x][0],l,mid,ll,rr);
else if(ll>mid) return check(ch[x][1],mid+1,r,ll,rr);
else return (check(ch[x][0],l,mid,ll,mid)+check(ch[x][1],mid+1,r,mid+1,rr))%mod;
}
int main()
{
int r,c,k;
scanf("%d%d%d",&r,&c,&k);
int i,j;
for(i=1;i<=r;i++)
{
for(j=1;j<=c;j++)
{
scanf("%d",&a[i][j]);
if(i!=1&&j!=1)
f[i][j]=(pre[i-1][j-1]-check(rt[a[i][j]],1,c,1,j-1))%mod;
else if(i==1&&j==1) f[i][j]=1;
}
for(j=1;j<=c;j++)
{
pre[i][j]=(((pre[i][j-1]+pre[i-1][j])%mod-pre[i-1][j-1])%mod+f[i][j])%mod;
addnew(rt[a[i][j]],1,c,j,f[i][j]);
}
}
printf("%d",(f[r][c]+mod)%mod);
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: