• LOJ3223 Trzy kule


    Trzy kule

    对于两个长度为 (n)(01)(a_1, a_2, dots, a_n)(b_1, b_2, dots, b_n),定义它们的距离 (d(a, b) = sum_{i=1}^{n} |a_i - b_i|)

    给定三个长度为 (n)(01)(s_1, s_2, s_3) 以及三个非负整数 (r_1, r_2, r_3),问有多少个长度为 (n)(01)(S) 满足 (d(S, s_1) le r_1, d(S, s_2) le r_2, d(S, s_3) le r_3) 这三个不等式中至少有一个成立。

    (1 le n le 10000, 0 le r_i le n)

    题解

    http://jklover.hs-blog.cf/2020/06/09/Loj-3223-Trzy-kule/#more

    Meet in the middle + 二维前缀和.

    首先可以补集转化一下,用 (2^n) 减掉三个不等式都不成立的方案数.

    将每个 (r) 变为 (n-1-r) ,则我们需要求出与每个串相同的字符数都不超过对应的 (r) 的方案数.

    首先可以将这三个串都异或上第一个串,答案不变,于是一个位置只会有 (000,001,010,011) 这四种情况.

    记这四种情况分别有(k_0,k_1,k_2,k_3)个,再记串 (S) 中,这四种位置上 (0) 的数目分别为 (c_0,c_1,c_2,c_3).

    显然要满足限制:

    1. (c_0+c_1+c_2+c_3leq r_0)

    2. (c_0+c_1+k_2-c_2+k_3-c_3leq r_1)

    3. (c_0+k_1-c_1+c_2+k_3-c_3leq r_3)

    考虑 Meet in the middle, 先枚举 (c_0,c_1) ,再枚举 (c_2,c_3) ,询问能凑成合法四元组的 (c_0,c_1) 的贡献总和.

    看上去有 3 个维度的限制,但是 (c_0,c_1) 对应的点中有 2 维是一样的,于是可以缩成 2 维,变成单点加,最后矩形求和.

    由于每个维度的坐标都不会超过 (n) ,所以直接用二维前缀和处理即可.

    时间复杂度(O(n^2))

    CO int N=1e4+10;
    int fac[N],ifac[N],lim[3];
    char str[3][N];
    int cnt[4],iter[4];
    int sum[N][N];
    
    IN int C(int n,int m){
    	return mul(fac[n],mul(ifac[m],ifac[n-m]));
    }
    int main(){
    	int n=read<int>();
    	fac[0]=1;
    	for(int i=1;i<=n;++i) fac[i]=mul(fac[i-1],i);
    	ifac[n]=fpow(fac[n],mod-2);
    	for(int i=n-1;i>=0;--i) ifac[i]=mul(ifac[i+1],i+1);
    	for(int i=0;i<3;++i){
    		lim[i]=n-1-read<int>();
    		if(lim[i]<0){ // r=n
    			printf("%d
    ",fpow(2,n));
    			return 0;
    		}
    		scanf("%s",str[i]+1);
    	}
    	fac[0]=1;
    	for(int i=1;i<=n;++i){
    		int x=str[0][i]-'0',y=str[1][i]-'0',z=str[2][i]-'0';
    		y^=x,z^=x;
    		cnt[y<<1|z]++; // notice
    	}
    	int w=0,h=0; // edit 1: TLE
    	for(iter[0]=0;iter[0]<=cnt[0];++iter[0])for(iter[1]=0;iter[1]<=cnt[1];++iter[1]){
    		int x=iter[0]+iter[1]; // matching for str[0] and str[1]
    		if(x>lim[0] or x>lim[1]) continue;
    		int y=iter[0]+cnt[1]-iter[1]; // matching for str[2]
    		if(y>lim[2]) continue;
    		w=max(w,x),h=max(h,y);
    		sum[x][y]=add(sum[x][y],mul(C(cnt[0],iter[0]),C(cnt[1],iter[1])));
    	}
    	for(int i=0;i<=w;++i)for(int j=0;j<=h;++j){
    		if(i) sum[i][j]=add(sum[i][j],sum[i-1][j]);
    		if(j) sum[i][j]=add(sum[i][j],sum[i][j-1]);
    		if(i and j) sum[i][j]=add(sum[i][j],mod-sum[i-1][j-1]);
    	}
    	int ans=0;
    	for(iter[2]=0;iter[2]<=cnt[2];++iter[2])for(iter[3]=0;iter[3]<=cnt[3];++iter[3]){
    		int x=min(lim[0]-iter[2]-iter[3],lim[1]-(cnt[2]-iter[2])-(cnt[3]-iter[3])); // limit for str[0] and str[1]
    		if(x<0) continue;
    		int y=lim[2]-iter[2]-(cnt[3]-iter[3]); // limit for str[2]
    		if(y<0) continue;
    		x=min(x,w),y=min(y,h);
    		ans=add(ans,mul(sum[x][y],mul(C(cnt[2],iter[2]),C(cnt[3],iter[3]))));
    	}
    	ans=add(fpow(2,n),mod-ans);
    	printf("%d
    ",ans);
    	return 0;
    }
    
  • 相关阅读:
    我的架构经验系列文章 前端架构
    我的架构经验系列文章 后端架构 安全层面
    Adhesive框架系列文章报警服务模块使用和实现
    Adhesive框架系列文章Mongodb数据服务模块实现(上)
    在Silverlight程序中使用Thread一个很容易被忽略的问题
    .net(偏web) vs j2ee的一些框架选型
    Wcf扩展
    Adhesive框架系列文章内存队列服务模块使用和实现
    【翻译】C#编程语言和JAVA编程语言的比较(下)
    在测试Adhesive的时候发现一个Mongodb官方驱动1.1.0.4184比较严重的BUG
  • 原文地址:https://www.cnblogs.com/autoint/p/13145120.html
Copyright © 2020-2023  润新知