Challenge: Costly Graphs
Subdomeniu: Combinatorics (combinatorics)
Scor cont: 120.0 / 120
Submission status: Accepted
Submission score: 1.0
Submission ID: 464834543
Limbaj: cpp14
Link challenge: https://www.hackerrank.com/challenges/costly-graphs/problem
Cerinta
Let's define the _cost of a simple undirected graph_ as the sum of the costs of its nodes. The _cost of a node_ is defined as _D_<sup>_K_</sup>, where _D_ is its degree.
You are given _N_ and _K_. You need to find the sum of the costs of all possible simple undirected graphs with _N_ nodes. As this number may be very large, output the sum modulo _1005060097_.
**Definitions**
Here are a few definitions from graph theory in case you're not familiar with them.
An _undirected graph_ is an ordered pair (_V_, _E_) consisting of a set _V_ of _nodes_, and a set _E_ of _edges_ which consists of unordered pairs of nodes from _V_.
The _degree_ of a node is the number of edges incident to it.
A _simple undirected graph_ is an undirected graph with no loops and multiple edges. A _loop_ is an edge connecting a node to itself. _Multiple edges_ are two or more edges connecting the same pair of nodes.
**Input Format**
The first line contains the number of test cases _T_.
Each of the next _T_ lines contains two integers _N_ and _K_ separated by a space.
**Output Format**
For each test case, output one line containing the sum of the costs of all possible simple undirected graphs with _N_ nodes, modulo _1005060097_.
**Constraints**
1 ≤ _T_ ≤ 2·10<sup>5</sup>
1 ≤ _N_ ≤ 10<sup>9</sup>
1 ≤ _K_ ≤ 2·10<sup>5</sup>
The sum of the _K_'s in a single test file is at most 2·10<sup>5</sup>.
**Sample input**
5
1 1
2 3
3 2
6 5
20 20
**Sample Output**
0
2
36
67584000
956922563
**Explanation**
In the first case, there is only one simple graph with 1 node, and the cost of that graph is 0<sup>1</sup> = 0.
In the second case, there are two simple graphs with 2 nodes, one with a single edge and one with no edges.
The cost of the graph with a single edge is 1<sup>3</sup>+1<sup>3</sup> = 2.
The cost of the graph with no edges is 0<sup>3</sup>+0<sup>3</sup> = 0.
Thus, the total is 2+0 = 2.
In the third case, there are eight simple graphs with 3 nodes.
There is one graph with three edges, and its cost is 2<sup>2</sup>+2<sup>2</sup>+2<sup>2</sup> = 12.
There are three graphs with two edges, and the cost of each is 1<sup>2</sup>+1<sup>2</sup>+2<sup>2</sup> = 6.
There are three graphs with one edge, and the cost of each is 0<sup>2</sup>+1<sup>2</sup>+1<sup>2</sup> = 2.
There is one graph with no edges, and its cost is 0<sup>2</sup>+0<sup>2</sup>+0<sup>2</sup> = 0.
Thus, the total is 12·1 + 6·3 + 2·3 + 0·1 = 36.
Cod sursa
#include <bits/stdc++.h>
using namespace std;
// Costly Graphs - HackerRank
// Sum over all simple graphs on N labeled vertices of sum_v deg(v)^K (mod P).
static const int MOD = 1005060097; // required modulus
static const int PRIMITIVE_ROOT = 5; // primitive root for MOD
static inline int mulmod(long long a, long long b) { return (int)((a * b) % MOD); }
static long long mod_pow(long long a, long long e) {
long long r = 1 % MOD;
a %= MOD;
while (e > 0) {
if (e & 1) r = r * a % MOD;
a = a * a % MOD;
e >>= 1;
}
return r;
}
static inline int mod_inv(int a) { return (int)mod_pow(a, MOD - 2); }
static void ntt(vector<long long>& a, bool invert) {
int n = (int)a.size();
for (int i = 1, j = 0; i < n; i++) {
int bit = n >> 1;
for (; j & bit; bit >>= 1) j ^= bit;
j ^= bit;
if (i < j) swap(a[i], a[j]);
}
for (int len = 2; len <= n; len <<= 1) {
long long wlen = mod_pow(PRIMITIVE_ROOT, (MOD - 1) / len);
if (invert) wlen = mod_pow(wlen, MOD - 2);
for (int i = 0; i < n; i += len) {
long long w = 1;
int half = len >> 1;
for (int j = 0; j < half; j++) {
long long u = a[i + j];
long long v = a[i + j + half] * w % MOD;
a[i + j] = u + v;
if (a[i + j] >= MOD) a[i + j] -= MOD;
a[i + j + half] = u - v;
if (a[i + j + half] < 0) a[i + j + half] += MOD;
w = w * wlen % MOD;
}
}
}
if (invert) {
long long inv_n = mod_pow(n, MOD - 2);
for (int i = 0; i < n; i++) a[i] = a[i] * inv_n % MOD;
}
}
static vector<int> convolution(const vector<int>& a, const vector<int>& b) {
if (a.empty() || b.empty()) return {};
int n1 = (int)a.size(), n2 = (int)b.size();
int n = 1;
while (n < n1 + n2 - 1) n <<= 1;
vector<long long> fa(n), fb(n);
for (int i = 0; i < n1; i++) fa[i] = a[i];
for (int i = 0; i < n2; i++) fb[i] = b[i];
ntt(fa, false);
ntt(fb, false);
for (int i = 0; i < n; i++) fa[i] = fa[i] * fb[i] % MOD;
ntt(fa, true);
vector<int> res(n1 + n2 - 1);
for (int i = 0; i < (int)res.size(); i++) res[i] = (int)fa[i];
return res;
}
// F(n,k) = sum_{d=0..n} C(n,d) d^k (mod MOD)
// via Stirling S(k,t) and identity:
// sum_d C(n,d) (d)_t = (n)_t * 2^{n-t}
static int calc_F(long long n, int k, const vector<int>& fact, const vector<int>& invfact) {
if (k == 0) return (int)mod_pow(2, n);
// Stirling numbers S(k,t) computed by convolution:
// S(k,t) = 1/t! * sum_{j=0..t} (-1)^{t-j} C(t,j) j^k
// This equals convolution of sequences:
// A[j] = j^k / j!, B[j] = (-1)^j / j!
vector<int> A(k + 1), B(k + 1);
for (int i = 0; i <= k; i++) {
long long p = (i == 0 ? 0 : mod_pow(i, k)); // k>=1 in constraints
A[i] = mulmod(p, invfact[i]);
B[i] = invfact[i];
if (i & 1) B[i] = (B[i] == 0 ? 0 : MOD - B[i]);
}
vector<int> stir = convolution(A, B); // stir[t] = S(k,t)
int limit = (int)min<long long>(n, k);
long long pow2 = mod_pow(2, n);
long long inv2 = mod_inv(2);
long long falling = 1; // (n)_0
long long res = 0;
for (int t = 0; t <= limit; t++) {
long long s2 = (t < (int)stir.size()) ? stir[t] : 0;
res += s2 * falling % MOD * pow2 % MOD;
res %= MOD;
if (t < limit) {
falling = falling * ((n - t) % MOD) % MOD; // (n)_{t+1}
pow2 = pow2 * inv2 % MOD; // 2^{n-(t+1)}
}
}
return (int)res;
}
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
int T;
cin >> T;
vector<long long> Ns(T);
vector<int> Ks(T);
int maxK = 0;
for (int i = 0; i < T; i++) {
long long N;
int K;
cin >> N >> K;
Ns[i] = N;
Ks[i] = K;
maxK = max(maxK, K);
}
// factorials up to maxK
vector<int> fact(maxK + 1), invfact(maxK + 1);
fact[0] = 1;
for (int i = 1; i <= maxK; i++) fact[i] = mulmod(fact[i - 1], i);
invfact[maxK] = mod_inv(fact[maxK]);
for (int i = maxK; i >= 1; i--) invfact[i - 1] = mulmod(invfact[i], i);
for (int qi = 0; qi < T; qi++) {
long long N = Ns[qi];
int K = Ks[qi];
if (N == 1) {
cout << 0 << "\n";
continue;
}
long long n = N - 1;
long long exponent = n * (n - 1) / 2; // edges among the other vertices
long long base = (N % MOD) * mod_pow(2, exponent) % MOD;
int F = calc_F(n, K, fact, invfact);
long long ans = base * F % MOD;
cout << ans << "\n";
}
return 0;
}
HackerRank Combinatorics – Costly Graphs
