[CF 1425D]Danger of Mad Snakes(组合计数+容斥)

题目链接

题意:

有一个 1000 × 1000 1000\times 1000 1000×1000的方格。有 n n n条蛇在方格中,每条蛇告诉你在方格中的坐标 ( X i , Y i ) (X_{i},Y_{i}) (Xi,Yi)和一个权值 B i B_{i} Bi
然后任意选其中 m m m条蛇,对于每条被选中的蛇,它和所有满足 m a x ( ∣ X ′ − X ∣ , ∣ Y ′ − Y ∣ ) < = R max(|X'-X|,|Y'-Y|)<=R max(XX,YY)<=R的位于 ( X ′ , Y ′ ) (X',Y') (X,Y)的蛇都会被杀死。
对于其中一次选法,它的权值是所有在该选法中被杀死的蛇的权值和的平方。
求所有选法的权值总和。

思路:

考虑权值和的平方 ( B i + B j + B k ) 2 (B_{i}+B_{j}+B_{k})^{2} (Bi+Bj+Bk)2,我们把平方式拆开变成 B i 2 + B j 2 + B k 2 + 2 B i B j + 2 B i B k + 2 B j B k B_{i}^{2}+B_{j}^{2}+B_{k}^{2}+2B_{i}B_{j}+2B_{i}B_{k}+2B_{j}B_{k} Bi2+Bj2+Bk2+2BiBj+2BiBk+2BjBk

考虑计算单个平方项 B i 2 B_{i}^{2} Bi2的贡献,就是每条蛇被选取的总次数 × B i 2 \times B_{i}^{2} ×Bi2
我们计算一下 情况总数 − - 没有被选到的情况数 就行了。

考虑计算两项相乘项 2 B i B j 2B_{i}B_{j} 2BiBj的贡献,就是两条蛇都被选取的次数 × 2 B i B j \times 2B_{i}B_{j} ×2BiBj
这个我们容斥一下,计算 两条都不被选取的情况数+ i i i被选取的情况数+ j j j被选取的情况数-总情况数 就行了。

代码:

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll mod = 1000000007;

ll fpow(ll a,ll n)
{
    ll sum=1,base=a%mod;
    while(n!=0)
    {
        if(n%2)sum=sum*base%mod;
        base=base*base%mod;
        n/=2;
    }
    return sum;
}
ll inv(ll a)
{
    return fpow(a,mod-2);
}
ll jie[1000005],rjie[1000005];
void initJie(ll n)
{
    jie[0]=1;
    for(ll i=1;i<=n;i++)jie[i]=jie[i-1]*i%mod;
    for(ll i=0;i<=n;i++)rjie[i]=inv(jie[i]);
}
ll C(ll n,ll k)
{
    if(k>n)return 0;
    else return jie[n]*rjie[k]%mod*rjie[n-k]%mod;
}
ll n,m,r;
ll x[2005],y[2005],b[2005];
bitset<2005>num[2005],tmp;
ll get1(ll i)
{
    return (b[i]*b[i]%mod*(C(n,m)-C(n-num[i].count(),m))%mod+mod)%mod;
}
ll get2(ll i,ll j)
{
    tmp=num[i]|num[j];
    return 2*b[i]*b[j]%mod*(C(n-tmp.count(),m)+(C(n,m)-C(n-num[i].count(),m))-C(n-num[j].count(),m))%mod;
}

int main()
{
    initJie(100000);
    scanf("%lld%lld%lld",&n,&m,&r);
    for(ll i=1;i<=n;i++)
    {
        scanf("%lld%lld%lld",&x[i],&y[i],&b[i]);
    }
    for(ll i=1;i<=n;i++)
    {
        for(ll j=1;j<=n;j++)
        {
            if(max(abs(x[j]-x[i]),abs(y[j]-y[i]))<=r)
            {
                num[i].set(j);
             }
        }
    }
    ll ans=0;
    for(ll i=1;i<=n;i++)
    {
        ans=(ans+get1(i))%mod;
    }
    for(ll i=1;i<=n;i++)
    {
        for(ll j=i+1;j<=n;j++)
        {
            ans=(ans+get2(i,j))%mod;
        }
    }
    printf("%lld\n",(ans%mod+mod)%mod);
    return 0;
}

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值