281. Java 5, 11628 байт, A000947
// package oeis_challenge;
import java.util.*;
import java.lang.*;
class Main {
// static void assert(boolean cond) {
// if (!cond)
// throw new Error("Assertion failed!");
// }
/* Use the formula a(n) = A000063(n + 2) - A000936(n).
It's unfair that I use the formula of "number of free polyenoid with n
nodes and symmetry point group C_{2v}" (formula listed in A000063)
without understanding why it's true...
*/
static int catalan(int x) {
int ans = 1;
for (int i = 1; i <= x; ++i)
ans = ans * (2*x+1-i) / i;
return ans / -~x;
}
static int A63(int n) {
int ans = catalan(n/2 - 1);
if (n%4 == 0) ans -= catalan(n/4 - 1);
if (n%6 == 0) ans -= catalan(n/6 - 1);
return ans;
}
static class Point implements Comparable<Point> {
final int x, y;
Point(int _x, int _y) {
x = _x; y = _y;
}
/// @return true if this is a point, false otherwise (this is a vector)
public boolean isPoint() {
return (x + y) % 3 != 0;
}
/// Translate this point by a vector.
public Point add(Point p) {
assert(this.isPoint() && ! p.isPoint());
return new Point(x + p.x, y + p.y);
}
/// Reflect this point along x-axis.
public Point reflectX() {
return new Point(x - y, -y);
}
/// Rotate this point 60 degrees counter-clockwise.
public Point rot60() {
return new Point(x - y, x);
}
@Override
public boolean equals(Object o) {
if (!(o instanceof Point)) return false;
Point p = (Point) o;
return x == p.x && y == p.y;
}
@Override
public int hashCode() {
return 21521 * (3491 + x) + y;
}
public String toString() {
// return String.format("(%d, %d)", x, y);
return String.format("setxy %d %d", x * 50 - y * 25, y * 40);
}
public int compareTo(Point p) {
int a = Integer.valueOf(x).compareTo(p.x);
if (a != 0) return a;
return Integer.valueOf(y).compareTo(p.y);
}
/// Helper class.
static interface Predicate {
abstract boolean test(Point p);
}
static abstract class UnaryFunction {
abstract Point apply(Point p);
}
}
static class Edge implements Comparable<Edge> {
final Point a, b; // guarantee a < b
Edge(Point x, Point y) {
assert x != y;
if (x.compareTo(y) > 0) { // y < x
a = y; b = x;
} else {
a = x; b = y;
}
}
public int compareTo(Edge e) {
int x = a.compareTo(e.a);
if (x != 0) return x;
return b.compareTo(e.b);
}
}
/// A graph consists of multiple {@code Point}s.
static class Graph {
private HashMap<Point, Point> points;
public Graph() {
points = new HashMap<Point, Point>();
}
public Graph(Graph g) {
points = new HashMap<Point, Point>(g.points);
}
public void add(Point p, Point root) {
assert(p.isPoint());
assert(root.isPoint());
assert(p == root || points.containsKey(root));
points.put(p, root);
}
public Graph map(Point.UnaryFunction fn) {
Graph result = new Graph();
for (Map.Entry<Point, Point> pq : points.entrySet()) {
Point p = pq.getKey(), q = pq.getValue();
assert(p.isPoint()) : p;
assert(q.isPoint()) : q;
p = fn.apply(p); assert(p.isPoint()) : p;
q = fn.apply(q); assert(q.isPoint()) : q;
result.points.put(p, q);
}
return result;
}
public Graph reflectX() {
return this.map(new Point.UnaryFunction() {
public Point apply(Point p) {
return p.reflectX();
}
});
}
public Graph rot60() {
return this.map(new Point.UnaryFunction() {
public Point apply(Point p) {
return p.rot60();
}
});
}
@Override
public boolean equals(Object o) {
if (o == null) return false;
if (o.getClass() != getClass()) return false;
Graph g = (Graph) o;
return points.equals(g.points);
}
@Override
public int hashCode() {
return points.hashCode();
}
Graph[] expand(Point.Predicate fn) {
List<Graph> result = new ArrayList<Graph>();
for (Point p : points.keySet()) {
int[] deltaX = new int[] { -1, 0, 1, 1, 0, -1};
int[] deltaY = new int[] { 0, 1, 1, 0, -1, -1};
for (int i = 6; i --> 0;) {
Point p1 = new Point(p.x + deltaX[i], p.y + deltaY[i]);
if (points.containsKey(p1) || !fn.test(p1)
|| !p1.isPoint()) continue;
Graph g = new Graph(this);
g.add(p1, p);
result.add(g);
}
}
return result.toArray(new Graph[0]);
}
public static Graph[] expand(Graph[] graphs, Point.Predicate fn) {
Set<Graph> result = new HashSet<Graph>();
for (Graph g0 : graphs) {
Graph[] g = g0.expand(fn);
for (Graph g1 : g) {
if (result.contains(g1)) continue;
result.add(g1);
}
}
return result.toArray(new Graph[0]);
}
private Edge[] edges() {
List<Edge> result = new ArrayList<Edge>();
for (Map.Entry<Point, Point> pq : points.entrySet()) {
Point p = pq.getKey(), q = pq.getValue();
if (p.equals(q)) continue;
result.add(new Edge(p, q));
}
return result.toArray(new Edge[0]);
}
/**
* Check if two graphs are isomorphic... under translation.
* @return {@code true} if {@code this} is isomorphic
* under translation, {@code false} otherwise.
*/
public boolean isomorphic(Graph g) {
if (points.size() != g.points.size()) return false;
Edge[] a = this.edges();
Edge[] b = g.edges();
Arrays.sort(a);
Arrays.sort(b);
// for (Edge e : b)
// System.err.println(e.a + " - " + e.b);
// System.err.println("------- >><< ");
assert (a.length > 0);
assert (a.length == b.length);
int a_bx = a[0].a.x - b[0].a.x, a_by = a[0].a.y - b[0].a.y;
for (int i = 0; i < a.length; ++i) {
if (a_bx != a[i].a.x - b[i].a.x ||
a_by != a[i].a.y - b[i].a.y) return false;
if (a_bx != a[i].b.x - b[i].b.x ||
a_by != a[i].b.y - b[i].b.y) return false;
}
return true;
}
// C_{2v}.
public boolean correctSymmetry() {
Graph[] graphs = new Graph[6];
graphs[0] = this.reflectX();
for (int i = 1; i < 6; ++i) graphs[i] = graphs[i-1].rot60();
assert(graphs[5].rot60().isomorphic(graphs[0]));
int count = 0;
for (Graph g : graphs) {
if (this.isomorphic(g)) ++count;
// if (count >= 2) {
// return false;
// }
}
// if (count > 1) System.err.format("too much: %d%n", count);
assert(count > 0);
return count == 1; // which is, basically, true
}
public void reflectSelfType2() {
Graph g = this.map(new Point.UnaryFunction() {
public Point apply(Point p) {
return new Point(p.y - p.x, p.y);
}
});
Point p = new Point(1, 1);
assert (p.equals(points.get(p)));
points.putAll(g.points);
assert (p.equals(points.get(p)));
Point q = new Point(0, 1);
assert (q.equals(points.get(q)));
points.put(p, q);
}
public void reflectSelfX() {
Graph g = this.reflectX();
points.putAll(g.points); // duplicates doesn't matter
}
}
static int A936(int n) {
// if (true) return (new int[]{0, 0, 0, 1, 1, 2, 4, 4, 12, 10, 29, 27, 88, 76, 247, 217, 722, 638, 2134, 1901, 6413})[n];
// some unreachable codes here for testing.
int ans = 0;
if (n % 2 == 0) { // reflection type 2. (through line 2x == y)
Graph[] graphs = new Graph[1];
graphs[0] = new Graph();
Point p = new Point(1, 1);
graphs[0].add(p, p);
for (int i = n / 2 - 1; i --> 0;)
graphs = Graph.expand(graphs, new Point.Predicate() {
public boolean test(Point p) {
return 2*p.x > p.y;
}
});
int count = 0;
for (Graph g : graphs) {
g.reflectSelfType2();
if (g.correctSymmetry()) {
++count;
// for (Edge e : g.edges())
// System.err.println(e.a + " - " + e.b);
// System.err.println("------*");
}
// else System.err.println("Failed");
}
assert (count%2 == 0);
// System.err.println("A936(" + n + ") count = " + count + " -> " + (count/2));
ans += count / 2;
}
// Reflection type 1. (reflectX)
Graph[] graphs = new Graph[1];
graphs[0] = new Graph();
Point p = new Point(1, 0);
graphs[0].add(p, p);
if (n % 2 == 0) graphs[0].add(new Point(2, 0), p);
for (int i = (n-1) / 2; i --> 0;)
graphs = Graph.expand(graphs, new Point.Predicate() {
public boolean test(Point p) {
return p.y > 0;
}
});
int count = 0;
for (Graph g : graphs) {
g.reflectSelfX();
if (g.correctSymmetry()) {
++count;
// for (Edge e : g.edges())
// System.err.printf(
// "pu %s pd %s\n"
// // "%s - %s%n"
// , e.a, e.b);
// System.err.println("-------/");
}
// else System.err.println("Failed");
}
if(n % 2 == 0) {
assert(count % 2 == 0);
count /= 2;
}
ans += count;
// System.err.println("A936(" + n + ") = " + ans);
return ans;
}
public static void main(String[] args) {
// Probably
if (! "1.5.0_22".equals(System.getProperty("java.version"))) {
System.err.println("Warning: Java version is not 1.5.0_22");
}
// A936(6);
for (int i = 0; i < 20; ++i)
System.out.println(i + " | " + (A63(i+9) - A936(i+7)));
//A936(i+2);
}
}
Спробуйте в Інтернеті!
Бічна примітка:
- Тестується локально за допомогою Java 5. (так, що попередження не друкується - див. Вкладку налагодження TIO)
- Не варто. Колись. Використовуйте. Java. 1. Це більш багатослівно, ніж Java взагалі.
Це може зламати ланцюг.
- Розрив (7 днів і 48 хвилин) не більше ніж проміжок, створений цією відповіддю , який на 7 днів і 1 годину 25 хвилин пізніше попереднього .
Новий рекорд на великий рахунок! Оскільки я (помилково?) Використовую пробіли замість вкладок, кількість рахунків перевищує необхідну. На моїй машині це 9550 байт. (на момент написання цієї редакції)
- Наступна послідовність .
- Код у своєму нинішньому вигляді друкує лише перші 20 термінів послідовності. Однак її легко змінити, щоб вона надрукувала перші 1000 елементів (змінивши
20вказівку for (int i = 0; i < 20; ++i)на 1000)
Так! Це може обчислити більше термінів, ніж зазначено на сторінці OEIS! (вперше для завдання мені потрібно використовувати Java), якщо в OEIS десь більше термінів ...
Швидке пояснення
Пояснення опису послідовності.
Послідовність запитують кількість вільного непланарного поліеноїда з симетрією групи C 2v , де:
- поліеноїд: (математична модель поліенових вуглеводнів) дерева (або в виродженому випадку, одна вершина) з можуть бути вбудовані в шестикутну решітку.
Наприклад, розглянемо дерева
O O O O (3)
| \ / \
| \ / \
O --- O --- O O --- O O --- O
| \
| (2) \
(1) O O
Перший не може бути вбудований у шестикутну решітку, а другий -. Саме конкретне вбудовування вважається відмінним від третього дерева.
- непланарний поліеноїд: вбудовування дерев таким чином, що існує дві вершини, що перекриваються.
(2)а (3)дерево вгорі планарне. Цей, однак, непланарний:
O---O O
/ \
/ \
O O
\ /
\ /
O --- O
(Є 7 вершин і 6 ребер)
- вільний поліеноїд: Варіанти одного поліеноїда, який можна отримати за допомогою обертання та відбиття, зараховують до одного.

- Група C 2v : Полієноїд рахується лише у тому випадку, якщо вони мають 2 перпендикулярні площини відбиття і не більше.
Наприклад, єдиний полієноїд з 2 вершинами
O --- O
має 3 площини відображення: горизонтальну -, вертикальну |та ту, паралельну екрану комп’ютера ■. Це занадто.
З іншого боку, цей
O --- O
\
\
O
має 2 площини відображення: /і ■.
Пояснення методу
А тепер підхід про те, як насправді підрахувати кількість.
По-перше, я вважаю формулу a(n) = A000063(n + 2) - A000936(n)(вказану на сторінці OEIS) як належне. Я не читав пояснення в газеті.
[TODO виправити цю частину]
Звичайно, рахувати площинні простіше, ніж рахувати непланарні. Ось що робить і папір.
Геометрично планарні полієноїди (без вершин, що перекриваються) перераховуються за допомогою комп’ютерного програмування. Таким чином, число геометрично неплоских полієноїдів стає доступним.
Отже ... програма підраховує кількість планарного поліеноїда і віднімає його від загального.
Оскільки дерево все одно є планарним, воно, очевидно, має ■площину відображення. Таким чином, умова зводиться до "підрахунку кількості дерева з віссю відображення у його 2D-зображенні".
Наївним способом було б сформувати всі дерева з nвузлами та перевірити правильність симетрії. Однак, оскільки ми хочемо лише знайти кількість дерев з віссю відбиття, ми можемо просто генерувати всі можливі півдерева на одній половині, відобразити їх через вісь, а потім перевірити правильність симетрії. Більше того, оскільки генеровані полієноїди є (планарними) деревами, він повинен торкнутися осі відбиття рівно один раз.
Функція public static Graph[] expand(Graph[] graphs, Point.Predicate fn)приймає масив графіків, кожен має nвузли, і виводить масив графа, кожен має n+1вузли, не рівні один одному (під перекладом) - таким, що доданий вузол повинен задовольняти предикату fn.
Розглянемо 2 можливі осі відображення: та, яка проходить через вершину та збігається з ребрами ( x = 0), та перша, що є перпендикулярною бісектрисою ребра ( 2x = y). Ми можемо взяти лише один із них, оскільки генеровані графіки в будь-якому випадку є ізоморфними.

Отже, для першої осі x = 0ми починаємо з базового графіка, що складається з одного вузла (1, 0)(у випадку, коли nце непарно) або двох вузлів з ребром між (1, 0) - (2, 0)(у випадку, якщо nпарне), а потім розгорнути вузли таким чином y > 0. Це робиться в розділі програми "Рефлексія типу 1", а потім для кожного створеного графіка відображайте (дзеркало) себе через вісь X x = 0( g.reflectSelfX()), а потім перевіряйте, чи має він правильну симетрію.
Однак зауважте, що, якщо nділиться на 2, таким чином ми рахували кожен графік двічі, оскільки ми також генеруємо його дзеркальне зображення по осі 2x = y + 3.

(зверніть увагу на 2 апельсинові)
Подібно до осі 2x = y, якщо (і лише якщо) nє рівним, ми починаємо з точки (1, 1), генеруємо такі графіки, які 2*x > yвідображають кожен з них над 2x = yвіссю ( g.reflectSelfType2()), з'єднуємось (1, 0)із ними (1, 1)та перевіряємо, чи правильна їх симетрія. Не забудьте поділити і на 2.