聯考20200722 T3 積木
阿新 • • 發佈:2020-07-23
分析:
先只考慮兩種顏色,假設分別有\(A,B\)個,發現答案為\(\binom{A+B}{A}\)
轉化為\(A*B\)的網格上只能向右向下走,從左上角到右下角的方案數
變成三種,分別有\(A,B,C\)個,答案為\(\binom{A+B+C}{A+B}\binom{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)\)
對於每個\(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\n",ans); }