CF1425D
一道计算方式很具有启发意义的题。
对于这种“选中产生贡献”的题,明显是需要对每个点算对总答案的贡献。但是由于“和的平方”的存在,这很难实现。
所以不妨在上面思路的基础上稍作改良:对每两个点算对总答案的贡献。不难发现,贡献的次数即为两个点都被选中的情况数。设
小贴士:
-
可以发现
的情况也可以套用上面的式子,就不用分类讨论了。 -
计算包含点的数量可以用前缀和实现。
-
要预处理逆元,否则会超时。
code:
点击查看代码
using namespace my_std;
const int N=2e3+7,M=-1,inf=0x3f3f3f3f,mod=1e9+7;
int n,m,k,x[N],y[N],c[N],f[N],inv[N],s[N][N];
const int mx=1e3;
inline int qpow(int x,int y){
int ret=1;
while(y){
if(y&1)
ret=1ll*ret*x%mod;
x=1ll*x*x%mod;
y>>=1;
}
return ret;
}
inline int Cnm(int x,int y){
if(x<0||y<0||x<y)
return 0;
return 1ll*f[x]*inv[y]%mod*inv[x-y]%mod;
}
inline int get_dis(int i,int j){
return max(abs(x[i]-x[j]),abs(y[i]-y[j]));
}
inline int calc(int i,int j){
int X1=min(min(x[i]+k,x[j]+k),mx),X2=max(max(x[i]-k,x[j]-k),1),Y1=min(min(y[i]+k,y[j]+k),mx),Y2=max(max(y[i]-k,y[j]-k),1);
if(X1<X2||Y1<Y2)
return 0;
return s[X1][Y1]-s[X1][Y2-1]-s[X2-1][Y1]+s[X2-1][Y2-1];
}
void solve(){
scanf("%d%d%d",&n,&m,&k);
for(int i=1;i<=n;i++){
scanf("%d%d%d",&x[i],&y[i],&c[i]);
s[x[i]][y[i]]++;
}
for(int i=1;i<=mx;i++){
for(int j=1;j<=mx;j++)
s[i][j]+=s[i-1][j];
}
for(int i=1;i<=mx;i++){
for(int j=1;j<=mx;j++)
s[i][j]+=s[i][j-1];
}
f[0]=1,inv[0]=1;
for(int i=1;i<=n;i++){
f[i]=1ll*f[i-1]*i%mod;
inv[i]=qpow(f[i],mod-2);
}
int ans=0;
for(int i=1;i<=n;i++){
for(int j=1;j<=n;j++){
if(i==j)
ans=(ans+1ll*c[i]*c[i]%mod*(Cnm(n,m)-Cnm(n-calc(i,i),m)+mod)%mod)%mod;
else
ans=(ans+1ll*c[i]*c[j]%mod*(Cnm(n,m)-Cnm(n-calc(i,i),m)-Cnm(n-calc(j,j),m)+Cnm(n-(calc(i,i)+calc(j,j)-calc(i,j)),m)+2ll*mod)%mod)%mod;
}
}
printf("%d\n",ans);
}
signed main(){
int t=1;
// scanf("%d",&t);
while(t--)
solve();
}