题目链接:https://atcoder.jp/contests/abc127/tasks/abc127_e

题目大意

  给定一个$N*M$的棋盘,二元组$(x, y),1 \leq x \leq N,1 \leq y \leq M$,表示棋盘上的某一个位置,现在要在棋盘上选 K 个不同的位置,记为$(x_1, y_1), (x_2, y_2), \dots, (x_K, y_K)$,选择的相应代价为$\sum_{i=1}^{K-1} \sum_{j=i+1}^K (|x_i - x_j| + |y_i - y_j|)$,输出所有可能方案的代价总和。

分析

  首先不难发现,x 和 y 是可以分开计算的,所以只需要求$\sum_{i=1}^{K-1} \sum_{j=i+1}^K |x_i - x_j|$即可,同理可计算$\sum_{i=1}^{K-1} \sum_{j=i+1}^K |y_i - y_j|$。
  假如我们固定棋盘上 2 个位置$x_{i_1, j_1}, x_{i_2, j_2}$不动,在这种情况下,有$\tbinom{N*M-2}{K-2}$种选择方案,换句话说就是$|x_{i_1, j_1} - x_{i_2, j_2}|$出现了$\tbinom{N*M-2}{K-2}$次。
  但枚举所有点对无疑是要超时的,为此我们可以枚举$d = |x_{i_1, j_1} - x_{i_2, j_2}|$,$d \in [1, K - 1]$。
  可以先找找规律,当 d == 1 时,看看有多少对$(x_{i_1, j_1}, x_{i_2, j_2})$是满足的。
  可以发现,只要两个点纵坐标差值为1,就都满足。
  于是当 d == 1 时,有$M^2 * (N - 1)$种$(x_{i_1, j_1}, x_{i_2, j_2})$满足$d == |x_{i_1, j_1} - x_{i_2, j_2}|$。
  以此类推,可以找出规律:对于每个 d,都有$d * M^2 * (N - d)$种$(x_{i_1, j_1}, x_{i_2, j_2})$满足$d == |x_{i_1, j_1} - x_{i_2, j_2}|$。
  那么对于每个 d,它对答案的贡献就为$\tbinom{N*M-2}{K-2} * d * M^2 * (N - d)$。
  把所有的累加起来即可。

代码如下

 #include <bits/stdc++.h>
using namespace std; #define INIT() ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
#define Rep(i,n) for (int i = 0; i < (n); ++i)
#define For(i,s,t) for (int i = (s); i <= (t); ++i)
#define rFor(i,t,s) for (int i = (t); i >= (s); --i)
#define ForLL(i, s, t) for (LL i = LL(s); i <= LL(t); ++i)
#define rForLL(i, t, s) for (LL i = LL(t); i >= LL(s); --i)
#define foreach(i,c) for (__typeof(c.begin()) i = c.begin(); i != c.end(); ++i)
#define rforeach(i,c) for (__typeof(c.rbegin()) i = c.rbegin(); i != c.rend(); ++i) #define pr(x) cout << #x << " = " << x << " "
#define prln(x) cout << #x << " = " << x << endl #define LOWBIT(x) ((x)&(-x)) #define ALL(x) x.begin(),x.end()
#define INS(x) inserter(x,x.begin()) #define ms0(a) memset(a,0,sizeof(a))
#define msI(a) memset(a,inf,sizeof(a))
#define msM(a) memset(a,-1,sizeof(a)) #define MP make_pair
#define PB push_back
#define ft first
#define sd second template<typename T1, typename T2>
istream &operator>>(istream &in, pair<T1, T2> &p) {
in >> p.first >> p.second;
return in;
} template<typename T>
istream &operator>>(istream &in, vector<T> &v) {
for (auto &x: v)
in >> x;
return in;
} template<typename T1, typename T2>
ostream &operator<<(ostream &out, const std::pair<T1, T2> &p) {
out << "[" << p.first << ", " << p.second << "]" << "\n";
return out;
} inline int gc(){
static const int BUF = 1e7;
static char buf[BUF], *bg = buf + BUF, *ed = bg; if(bg == ed) fread(bg = buf, , BUF, stdin);
return *bg++;
} inline int ri(){
int x = , f = , c = gc();
for(; c<||c>; f = c=='-'?-:f, c=gc());
for(; c>&&c<; x = x* + c - , c=gc());
return x*f;
} typedef long long LL;
typedef unsigned long long uLL;
typedef pair< double, double > PDD;
typedef pair< int, int > PII;
typedef pair< string, int > PSI;
typedef set< int > SI;
typedef vector< int > VI;
typedef vector< PII > VPII;
typedef map< int, int > MII;
typedef pair< LL, LL > PLL;
typedef vector< LL > VL;
typedef vector< VL > VVL;
const double EPS = 1e-;
const LL inf = 0x7fffffff;
const LL infLL = 0x7fffffffffffffffLL;
const LL mod = 1e9 + ;
const int maxN = 2e5 + ;
const LL ONE = ;
const LL evenBits = 0xaaaaaaaaaaaaaaaa;
const LL oddBits = 0x5555555555555555; LL fac[maxN];
void init_fact() {
fac[] = ;
For(i, , maxN - ) {
fac[i] = (i * fac[i - ]) % mod;
}
} //ax + by = gcd(a, b) = d
// 扩展欧几里德算法
/**
* a*x + b*y = 1
* 如果ab互质,有解
* x就是a关于b的逆元
* y就是b关于a的逆元
*
* 证明:
* a*x % b + b*y % b = 1 % b
* a*x % b = 1 % b
* a*x = 1 (mod b)
*/
inline void ex_gcd(LL a, LL b, LL &x, LL &y, LL &d){
if (!b) {d = a, x = , y = ;}
else{
ex_gcd(b, a % b, y, x, d);
y -= x * (a / b);
}
} // 求a关于p的逆元,如果不存在,返回-1
// a与p互质,逆元才存在
inline LL inv_mod(LL a, LL p = mod){
LL d, x, y;
ex_gcd(a, p, x, y, d);
return d == ? (x % p + p) % p : -;
} inline LL comb_mod(LL m, LL n) {
LL ret;
if(m > n) swap(m, n);
ret = (fac[n] * inv_mod(fac[m], mod)) % mod;
ret = (ret * inv_mod(fac[n - m], mod)) % mod;
return ret;
} void add_mod(LL &a, LL b) {
a = (a + b) % mod;
if(a < ) a += mod;
} int N, M, K;
LL ans; int main(){
INIT();
init_fact();
cin >> N >> M >> K;
LL cnt = comb_mod(K - , N * M - ); ForLL(d, , N - ) add_mod(ans, cnt * ((d * M * M * (N - d)) % mod));
ForLL(d, , M - ) add_mod(ans, cnt * ((d * N * N * (M - d)) % mod));
cout << ans << endl;
return ;
}

最新文章

  1. Struts2中跳转问题
  2. 通过刷bios的方式在win8.1平板上启动windows phone模拟器
  3. 用户编辑新建_AngularJS实现
  4. JVM 垃圾回收 Minor gc vs Major gc vs Full gc
  5. CSS中背景图片定位方法
  6. Cas Server中各配置文件介绍
  7. File类的基本操作之InputStream字节输入流
  8. 【读书笔记】《未来闪影》罗伯特&#183;J&#183;索耶
  9. 两个Hacker,专门Patch TObject
  10. HDU1716(全排列)
  11. 纯静态界面中(html)中通过js调用dll中的方法从数据库中读取数据
  12. 27.C++- 智能指针
  13. cmake安装配置及入门指南
  14. [PHP] 控制反转依赖注入的日常使用
  15. springboot的热部署
  16. OrCAD Capture CIS 16.6 将版本16.6的设计文件另存为版本16.2的设计文件
  17. 《剑指offer》-判断对称二叉树
  18. angularjs 1.x lazyloading
  19. MT【81】含参数三次函数因式分解
  20. 原生JS实现Promise

热门文章

  1. cf期望概率专题
  2. java 原生 HttpClient
  3. super_blocks没有导出
  4. HDU 6667 Roundgod and Milk Tea (思维)
  5. 【AI图像识别一】人脸识别测试探索
  6. centos7.4 系统优化
  7. squirrel sql client 连接phoenix
  8. PS--工具类
  9. Unity中调用Windows窗口句柄以及根据需求设置并且解决扩展屏窗体显示错乱/位置错误的Bug
  10. AXD 的使用以及源代码说明