zl程序教程

您现在的位置是:首页 >  其他

当前栏目

【CF809C】Find a car(动态规划)

规划 动态 Find Car
2023-09-11 14:14:40 时间

【CF809C】Find a car(动态规划)

题面

洛谷
CF
有一个无穷大的矩阵,第\(i\)行第\(j\)列的数是\((i-1)xor(j-1)+1\)\(q\)次询问,每次询问一个矩形内数小于等于\(k\)的数的和。

题解

询问等价于\(\sum_{i=l}^r\sum_{j=L}^R [i\oplus j\le k]i\oplus j\)
把询问拆分成四个从\(1\)开始的东西,即\([1..r,1..R],[1..l-1,1..R],[1..r,1..L-1],[1..l-1,1..L-1]\)
那么就可以大力数位\(dp\)了,设\(f[i][0/1][0/1][0/1]\)表示\(i\)是否卡在界上,\(j\)是否卡在界上,\(i\oplus j\)是否卡在界上,需要同时记录方案数和和。
大力转移一下就好了。

#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
#define MOD 1000000007
void add(int &x,int y){x+=y;if(x>=MOD)x-=MOD;}
inline int read()
{
	int x=0;bool t=false;char ch=getchar();
	while((ch<'0'||ch>'9')&&ch!='-')ch=getchar();
	if(ch=='-')t=true,ch=getchar();
	while(ch<='9'&&ch>='0')x=x*10+ch-48,ch=getchar();
	return t?-x:x;
}
int f[32][2][2][2],g[32][2][2][2];
int Calc(int L,int R,int K)
{
	if(L<0||R<0)return 0;
	memset(f,0,sizeof(f));memset(g,0,sizeof(g));f[31][0][0][0]=1;
	for(int i=30;~i;--i)
		for(int a=0;a<=1;++a)
			for(int b=0;b<=1;++b)
				for(int k=0;k<=1;++k)
				{
					if(!f[i+1][a][b][k])continue;
					for(int A=0;A<=1;++A)
						for(int B=0;B<=1;++B)
						{
							if(!a&&A&&!(L&(1<<i)))continue;
							if(!b&&B&&!(R&(1<<i)))continue;
							if(!k&&!(K&(1<<i))&&(A^B))continue;
							int na=a,nb=b,nk=k;
							if(!A&&(L&(1<<i)))na|=1;
							if(!B&&(R&(1<<i)))nb|=1;
							if(!(A^B)&&(K&(1<<i)))nk|=1;
							add(f[i][na][nb][nk],f[i+1][a][b][k]);
							add(g[i][na][nb][nk],g[i+1][a][b][k]);
							if(A^B)add(g[i][na][nb][nk],1ll*(1<<i)*f[i+1][a][b][k]%MOD);
						}
				}
	int ret=0;
	for(int a=0;a<=1;++a)
		for(int b=0;b<=1;++b)
			for(int k=0;k<=1;++k)
				add(ret,g[0][a][b][k]),add(ret,f[0][a][b][k]);
	return ret;
}
int main()
{
	int Q=read();
	while(Q--)
	{
		int x1=read()-1,x2=read()-1,y1=read()-1,y2=read()-1,k=read()-1;
		int ans1=(Calc(y1,y2,k)+Calc(x1-1,x2-1,k))%MOD;
		int ans2=(Calc(x1-1,y2,k)+Calc(y1,x2-1,k))%MOD;
		int ans=(ans1+MOD-ans2)%MOD;
		printf("%d\n",ans);
	}
	return 0;
}