联考20200722 T3 积木


分析:
先只考虑两种颜色,假设分别有(A,B)个,发现答案为(inom{A+B}{A})
转化为(A*B)的网格上只能向右向下走,从左上角到右下角的方案数
变成三种,分别有(A,B,C)个,答案为(inom{A+B+C}{A+B}inom{A+B}{A})
把网格变成(A*B*C)三维的就好了
两堆积木(i,j)的贡献为((-A_i,-B_i,-C_i))((A_j,B_j,C_j))的方案数
枚举是(O(n^2))的且难以优化
发现实际上(A,B,C)的范围很小,只有150
那么网格规模只会有(m=300)的大小
于是考虑把所有((-A_i,-B_i,-C_i))的点值都加上1,直接DP求出来最后停留在每个点的方案数
对于每个(i)得到停留在((A_i,B_i,C_i))的方案数,最后每个点减去自己到自己的方案再除以2就是答案了
直接暴力DP,复杂度(O(m^3))

#include<cstdio>
#include<cmath>
#include<cstring>
#include<iostream>
#include<algorithm>
#include<queue>
#include<set>
#include<map>
#include<vector>
#include<string>

#define maxn 200005
#define maxm 155
#define INF 0x3f3f3f3f
#define MOD 100000007
#define eps 1e-10

using namespace std;

inline long long getint()
{
	long long num=0,flag=1;char c;
	while((c=getchar())<'0'||c>'9')if(c=='-')flag=-1;
	while(c>='0'&&c<='9')num=num*10+c-48,c=getchar();
	return num*flag;
}

int n;
int a[maxn],b[maxn],c[maxn];
int f[maxm<<1][maxm<<1][maxm<<1];
int C[maxm<<3][maxm<<3];

inline int upd(int x){return x<MOD?x:x-MOD;}
inline int ksm(int num,int k)
{
	int ret=1;
	for(;k;k>>=1,num=1ll*num*num%MOD)if(k&1)ret=1ll*ret*num%MOD;
	return ret;
}

int main()
{
	n=getint();
	C[0][0]=1;
	for(int i=1;i<maxm<<3;i++)
	{
		C[i][0]=1;
		for(int j=1;j<=i;j++)C[i][j]=upd(C[i-1][j-1]+C[i-1][j]);
	}
	for(int i=1;i<=n;i++)
	{
		a[i]=getint(),b[i]=getint(),c[i]=getint();
		f[maxm-a[i]][maxm-b[i]][maxm-c[i]]++;
	}
	for(int i=1;i<maxm<<1;i++)for(int j=1;j<maxm<<1;j++)for(int k=1;k<maxm<<1;k++)
		f[i][j][k]=upd(upd(f[i][j][k]+f[i-1][j][k])+upd(f[i][j-1][k]+f[i][j][k-1]));
	int ans=0;
	for(int i=1;i<=n;i++)ans=(ans+1ll*(f[a[i]+maxm][b[i]+maxm][c[i]+maxm]-(1ll*C[2*(a[i]+b[i]+c[i])][2*(a[i]+b[i])]*C[2*(a[i]+b[i])][2*a[i]]%MOD)+MOD)*ksm(2,MOD-2))%MOD;
	printf("%d
",ans);
}

原文地址:https://www.cnblogs.com/Darknesses/p/13366937.html