积木 [DP+转化]

发现自己越训越菜啥DP都写不出来了。。

感觉这道DP属实有些神仙其实是我属实菜。好像全网都没啥题解的亚子

大意是给出 \(O(2e5)\) 个三元组 \((a,b,c)\) ,值域 \(150\) ,代表组内三种物品的数量。问随意挑出两组,其中物品构成的多重集排列数。

直接列式子全是下降幂跟阶乘,就很难受,拆不开。考虑必要的转化。

两个三元组 \((a_i,b_i,c_i)\)\((a_j,b_j,c_j)\) 构成的多重集排列数从路径计数的角度考虑,就是三维空间内从点 \((-a_i,-b_i,-c_i)\) 走到 \((a_j,b_j,c_j)\) 的方案数。

于是突然就可以DP了。当然枚举两个二元组每次做坐标DP肯定不行。可以合并DP,在每个起点处 \(+1\) ,做一遍DP即可。

最后减去自己与自己构成的方案,再除以 \(2\) 就好了。

God is Madoka
#include<bits/stdc++.h>
using namespace std;

namespace IO{
    typedef long long LL;
    typedef double DB;
    #define int LL
    int read(){
        int x=0,f=0; char ch=getchar();
        while(ch>'9'||ch<'0'){ f|=(ch=='-'); ch=getchar(); }
        while(ch>='0'&&ch<='9'){ x=(x<<1)+(x<<3)+(ch^48); ch=getchar(); }
        return f?-x:x;
    } char output[50];
    void write(int x,char sp){
        int len=0;
        if(x<0) putchar('-'), x=-x;
        do{ output[len++]=x%10+'0'; x/=10; }while(x);
        for(int i=len-1;~i;i--) putchar(output[i]); putchar(sp);
    }
    void ckmax(int& x,int y){ x=x>y?x:y; }
    void ckmin(int& x,int y){ x=x<y?x:y; }
} using namespace IO;

const int NN=200010,mod=1e8+7;
int n,ans,mxa,mxb,mxc,a[NN],b[NN],c[NN],f[310][310][310];

namespace Combinatorics{
    int fac[NN],inv[NN];
    int qpow(int a,int b=mod-2,int res=1){
        for(;b;b>>=1,a=a*a%mod)
            if(b&1) res=res*a%mod;
        return res;
    }
    void init(){
        fac[0]=inv[0]=1;
        for(int i=1;i<=1000;i++) fac[i]=fac[i-1]*i%mod;
        inv[1000]=qpow(fac[1000]);
        for(int i=999;i;i--) inv[i]=inv[i+1]*(i+1)%mod;
    }
} using namespace Combinatorics;

signed main(){
    freopen("cyj.in","r",stdin);
    freopen("cyj.out","w",stdout);
    n=read(); init();
    for(int i=1;i<=n;i++){
        ckmax(mxa,a[i]=read());
        ckmax(mxb,b[i]=read());
        ckmax(mxc,c[i]=read());
    }
    for(int i=1;i<=n;i++) ++f[mxa-a[i]][mxb-b[i]][mxc-c[i]];
    for(int i=0;i<=(mxa<<1);i++)
        for(int j=0;j<=(mxb<<1);j++)
            for(int k=0;k<=(mxc<<1);k++){
                if(i) f[i][j][k]=(f[i][j][k]+f[i-1][j][k])%mod;
                if(j) f[i][j][k]=(f[i][j][k]+f[i][j-1][k])%mod;
                if(k) f[i][j][k]=(f[i][j][k]+f[i][j][k-1])%mod;
            }
    for(int i=1;i<=n;i++){
        ans=(ans+f[mxa+a[i]][mxb+b[i]][mxc+c[i]])%mod;
        ans=(ans+mod-fac[a[i]+b[i]+c[i]<<1]*inv[a[i]<<1]%mod*inv[b[i]<<1]%mod*inv[c[i]<<1]%mod)%mod;
    }
    ans=ans*qpow(2)%mod;
    write(ans,'\n');
    return 0;
}
posted @ 2022-01-01 10:16  keen_z  阅读(33)  评论(0编辑  收藏  举报