Update all new Tasks
This commit is contained in:
parent
00a190b0a6
commit
91df62d461
5697 changed files with 93386 additions and 804 deletions
115
Task/Paraffins/C/paraffins-1.c
Normal file
115
Task/Paraffins/C/paraffins-1.c
Normal file
|
|
@ -0,0 +1,115 @@
|
|||
#include <stdio.h>
|
||||
|
||||
#define MAX_N 33 /* max number of tree nodes */
|
||||
#define BRANCH 4 /* max number of edges a single node can have */
|
||||
|
||||
/* The basic idea: a paraffin molecule can be thought as a simple tree
|
||||
with each node being a carbon atom. Counting molecules is thus the
|
||||
problem of counting free (unrooted) trees of given number of nodes.
|
||||
|
||||
An unrooted tree needs to be uniquely represented, so we need a way
|
||||
to cannonicalize equivalent free trees. For that, we need to first
|
||||
define the cannonical form of rooted trees. Since rooted trees can
|
||||
be constructed by a root node and up to BRANCH rooted subtrees that
|
||||
are arranged in some definite order, we can define it thusly:
|
||||
* Given the root of a tree, the weight of each of its branches is
|
||||
the number of nodes contained in that branch;
|
||||
* A cannonical rooted tree would have its direct subtrees ordered
|
||||
in descending order by weight;
|
||||
* In case multiple subtrees are the same weight, they are ordered
|
||||
by some unstated, but definite, order (this code doesn't really
|
||||
care what the ordering is; it only counts the number of choices
|
||||
in such a case, not enumerating individual trees.)
|
||||
|
||||
A rooted tree of N nodes can then be constructed by adding smaller,
|
||||
cannonical rooted trees to a root node, such that:
|
||||
* Each subtree has fewer than BRANCH branches (since it must have
|
||||
an empty slot for an edge to connect to the new root);
|
||||
* Weight of those subtrees added later are no higher than earlier
|
||||
ones;
|
||||
* Their weight total N-1.
|
||||
A rooted tree so constructed would be itself cannonical.
|
||||
|
||||
For an unrooted tree, we can define the radius of any of its nodes:
|
||||
it's the maximum weight of any of the subtrees if this node is used
|
||||
as the root. A node is the center of a tree if it has the smallest
|
||||
radius among all the nodes. A tree can have either one or two such
|
||||
centers; if two, they must be adjacent (cf. Knuth, tAoCP 2.3.4.4).
|
||||
|
||||
An important fact is that, a node in a tree is its sole center, IFF
|
||||
its radius times 2 is no greater than the sum of the weights of all
|
||||
branches (ibid). While we are making rooted trees, we can add such
|
||||
trees encountered to the count of cannonical unrooted trees.
|
||||
|
||||
A bi-centered unrooted tree with N nodes can be made by joining two
|
||||
trees, each with N/2 nodes and fewer than BRANCH subtrees, at root.
|
||||
The pair must be ordered in aforementioned implicit way so that the
|
||||
product is cannonical. */
|
||||
|
||||
typedef unsigned long long xint;
|
||||
#define FMT "llu"
|
||||
|
||||
xint rooted[MAX_N] = {1, 1, 0};
|
||||
xint unrooted[MAX_N] = {1, 1, 0};
|
||||
|
||||
/* choose k out of m possible values; chosen values may repeat, but the
|
||||
ordering of them does not matter. It's binomial(m + k - 1, k) */
|
||||
xint choose(xint m, xint k)
|
||||
{
|
||||
xint i, r;
|
||||
|
||||
if (k == 1) return m;
|
||||
for (r = m, i = 1; i < k; i++)
|
||||
r = r * (m + i) / (i + 1);
|
||||
return r;
|
||||
}
|
||||
|
||||
/* constructing rooted trees of BR branches at root, with at most
|
||||
N radius, and SUM nodes in the partial tree already built. It's
|
||||
recursive, and CNT and L carry down the number of combinations
|
||||
and the tree radius already encountered. */
|
||||
void tree(xint br, xint n, xint cnt, xint sum, xint l)
|
||||
{
|
||||
xint b, c, m, s;
|
||||
|
||||
for (b = br + 1; b <= BRANCH; b++) {
|
||||
s = sum + (b - br) * n;
|
||||
if (s >= MAX_N) return;
|
||||
|
||||
/* First B of BR branches are all of weight n; the
|
||||
rest are at most of weight N-1 */
|
||||
c = choose(rooted[n], b - br) * cnt;
|
||||
|
||||
/* This partial tree is singly centered as is */
|
||||
if (l * 2 < s) unrooted[s] += c;
|
||||
|
||||
/* Trees saturate at root can't be used as building
|
||||
blocks for larger trees, so forget them */
|
||||
if (b == BRANCH) return;
|
||||
rooted[s] += c;
|
||||
|
||||
/* Build the rest of the branches */
|
||||
for (m = n; --m; ) tree(b, m, c, s, l);
|
||||
}
|
||||
}
|
||||
|
||||
void bicenter(int s)
|
||||
{
|
||||
if (s & 1) return;
|
||||
|
||||
/* Pick two of the half-size building blocks, allowing
|
||||
repetition. */
|
||||
unrooted[s] += rooted[s/2] * (rooted[s/2] + 1) / 2;
|
||||
}
|
||||
|
||||
int main()
|
||||
{
|
||||
xint n;
|
||||
for (n = 1; n < MAX_N; n++) {
|
||||
tree(0, n, 1, 1, n);
|
||||
bicenter(n);
|
||||
printf("%"FMT": %"FMT"\n", n, unrooted[n]);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
106
Task/Paraffins/C/paraffins-2.c
Normal file
106
Task/Paraffins/C/paraffins-2.c
Normal file
|
|
@ -0,0 +1,106 @@
|
|||
#include <gmp.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#define MAX_BRANCH 4
|
||||
#define MAX_N 500
|
||||
|
||||
mpz_t bcache[MAX_N + 1];
|
||||
mpz_t ucache[MAX_N + 1];
|
||||
mpz_t *rcache[MAX_N + 1][MAX_BRANCH + 1];
|
||||
|
||||
mpz_t tmp1, tmp2;
|
||||
void choose(mpz_t r, mpz_t m, int k)
|
||||
{
|
||||
int i;
|
||||
mpz_set(r, m);
|
||||
|
||||
mpz_add_ui(tmp1, m, 1);
|
||||
for (i = 1; i < k; ) {
|
||||
mpz_mul(r, r, tmp1);
|
||||
mpz_divexact_ui(r, r, ++i);
|
||||
|
||||
if (i >= k) break;
|
||||
mpz_add_ui(tmp1, tmp1, 1);
|
||||
}
|
||||
}
|
||||
|
||||
mpz_t rtmp1, rtmp2;
|
||||
void calc_rooted(mpz_t res, int n, int b, int r)
|
||||
{
|
||||
mpz_set_ui(res, 0);
|
||||
|
||||
if (n == 1 && b == 0 && r == 0) {
|
||||
mpz_set_ui(res, 1);
|
||||
return;
|
||||
} else if (n <= b || n <= r || n == 1 || b == 0 || r == 0)
|
||||
return;
|
||||
|
||||
int b1, r1;
|
||||
for (b1 = 1; b1 <= b && r * b1 < n; b1++) {
|
||||
choose(rtmp1, bcache[r], b1);
|
||||
mpz_set_ui(rtmp2, 0);
|
||||
for (r1 = 0; r1 < r && r1 + r * b1 < n; r1++)
|
||||
mpz_add(rtmp2, rtmp2, rcache[n - r * b1][b - b1][r1]);
|
||||
|
||||
mpz_addmul(res, rtmp1, rtmp2);
|
||||
}
|
||||
}
|
||||
|
||||
void calc_first_branch(int n)
|
||||
{
|
||||
int b, r;
|
||||
mpz_init_set_ui(bcache[n], 0);
|
||||
|
||||
for (b = 0; b < MAX_BRANCH; b++)
|
||||
for (r = 0; r < n; r++)
|
||||
mpz_add(bcache[n], bcache[n], rcache[n][b][r]);
|
||||
}
|
||||
|
||||
void calc_unrooted(int n)
|
||||
{
|
||||
int b, r;
|
||||
|
||||
for (b = 0; b <= MAX_BRANCH; b++) {
|
||||
mpz_t *p = malloc(sizeof(mpz_t) * n);
|
||||
rcache[n][b] = p;
|
||||
for (r = 0; r < n; r++) {
|
||||
mpz_init(p[r]);
|
||||
calc_rooted(p[r], n, b, r);
|
||||
}
|
||||
}
|
||||
|
||||
calc_first_branch(n);
|
||||
|
||||
mpz_init_set_ui(ucache[n], 0);
|
||||
for (r = 0; r * 2 < n; r++)
|
||||
for (b = 0; b <= MAX_BRANCH; b++)
|
||||
mpz_add(ucache[n], ucache[n], rcache[n][b][r]);
|
||||
|
||||
if (!(n & 1)) {
|
||||
mpz_add_ui(rtmp1, bcache[n/2], 1);
|
||||
mpz_mul(rtmp1, rtmp1, bcache[n/2]);
|
||||
mpz_divexact_ui(rtmp1, rtmp1, 2);
|
||||
mpz_add(ucache[n], ucache[n], rtmp1);
|
||||
}
|
||||
}
|
||||
|
||||
void init(void)
|
||||
{
|
||||
mpz_init(tmp1), mpz_init(tmp2);
|
||||
mpz_init(rtmp1), mpz_init(rtmp2);
|
||||
}
|
||||
|
||||
int main(void)
|
||||
{
|
||||
int i;
|
||||
|
||||
init();
|
||||
|
||||
for (i = 0; i <= MAX_N; i++) {
|
||||
calc_unrooted(i);
|
||||
gmp_printf("%d: %Zd\n", i, ucache[i]);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue