cdq分治模板

#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
using namespace std;
#define MS(x,y) memset(x,y,sizeof(x))
#define MC(x,y) memcpy(x,y,sizeof(x))
#define MP(x,y) make_pair(x,y)
#define ls o<<1
#define rs o<<1|1
typedef long long LL;
typedef unsigned long long UL;
typedef unsigned int UI;
template inline void gmax(T1 &a,T2 b){if(b>a)a=b;}
template inline void gmin(T1 &a,T2 b){if(by(xb一定有a>1;
3,solve(l,m);
4,传递[l,m]对[m+1,r]的贡献。
5,solve(m+1,r);
这样cdq分治就做完啦
*/
int n,m;
int a[N][N];
int f[N][N];
int s[M];
void add(int &x,int y)
{
	x+=y;
	if(x>=Z)x-=Z;
}
void solve(int l,int r)
{
	if(l==r)return;
	int mid=(l+r)>>1;

	//步骤1,处理[l,mid]
	solve(l,mid);
	
	//步骤2,处理[l,mid]->(mid,r]的转移
	for(int i=l;i<=r;i++)for(int j=1;j<=m;j++)s[a[i][j]]=0;
	int all=0;
	//从左到右逐列枚举,以及逆拓扑序,保证了列的小于关系
	for(int j=1;j<=m;j++)
	{
		//行累加只在[l,mid]展开,行转移在(mid,r]收集,保证了行的小于关系
		for(int i=mid+1;i<=r;i++)add(f[i][j],(all-s[a[i][j]]+Z)%Z);
		for(int i=l;i<=mid;i++)
		{
			add(s[a[i][j]],f[i][j]);
			add(all,f[i][j]);
		}
	}
	
	//步骤3,处理(mid,r]
	solve(mid+1,r);
}
int main()
{
	while(~scanf("%d%d%*d",&n,&m))
	{
		for(int i=1;i<=n;i++)for(int j=1;j<=m;j++)scanf("%d",&a[i][j]);
		MS(f,0);f[1][1]=1;
		solve(1,n);
		printf("%d\n",f[n][m]);
	}
	return 0;
}
/*
【题意】
给你一个n(750)*m(750)的棋盘,每个格子(i,j)都有一种颜色c[i][j],
颜色范围是[1,top]中的整数,top取值范围是[1,5e5]。
对于一次跳跃A(y1,x1)->B(y2,x2),我们说这次跳跃是合法的,要求必须有:
1,y2>y1
2,x2>x1
3,c[y1][x1]!=c[y2][x2];
我们想要求出从(1,1)经过若干次跳跃之后到达(n,m)的方案数,mod(1e9+7)

【类型】
CDQ分治

【分析】
首先,我们可以构想一个暴力DP:
用f[i][j]表示从(1,1)到(i,j)的方案数
那么f[i][j]=∑f[u][v],u>1;
接下来只要依次实现以下3个步骤,这道题就做完了。
(1),solve(l,m);
(2),从[l,m]向[m+1,r]转移贡献;
(3),solve(m+1,r);
于是,关键落在要如何实现(2)上——
此时,我们发现行关系已经是严格的小于关系,只需要使得列关系满足要求。如何使得?
因为问题不是在数轴上,而是在矩形中。
所以目前处理的区间是一个块,行数是[l,r],每行有m列。
我们现在是想要把[l,mid]的状态传递到[mid+1,r]。
发现只需要从左向右一列列枚举。
状态结果的传递在(mid,r])实现,状态来源的累计在[l,mid]实现。
就保证了行与列都是严格小于关系,这道题也就在O(nmlogn)的时间复杂度内解决了。

*/

你可能感兴趣的:(cdq分治,模板)