Chiaki has a positive integer m and she would like to construct a tree with at most 15 vertices in such a manner that the number of distinct nonempty independent sets is exactly m.
Note that an independent set is a subset of vertices of a graph such that every two distinct vertices are not adjacent.
Input
There are multiple test cases. The first line of input contains an integer T (1 ≤ T ≤ 2000), indicating the number of test cases. For each test case:
The first line contains an integer m (1 ≤ m ≤ 2000).
Output
For each test case, if there is no such graph, output -1 on a single line. Otherwise, output an integer n (1 ≤ n ≤ 15) denoting the number of vertices. Then in each of the next n - 1 lines, output two integers x and y (1 ≤ x, y ≤ n, x ≠ y) denoting an edge in the tree.
Sample Input
5 1 2 3 10 20
Sample Output
1 2 2 1 -1 -1 6 2 1 3 1 4 3 5 4 6 5
Author: LIN, Xi
Source: The 17th Zhejiang University Programming Contest Sponsored by TuSimple
#include<stdio.h>
#include<iostream>
#include<string.h>
#include<string>
#include<ctype.h>
#include<math.h>
#include<set>
#include<map>
#include<vector>
#include<queue>
#include<bitset>
#include<algorithm>
#include<time.h>
using namespace std;
void fre() { freopen("c://test//input.in", "r", stdin); freopen("c://test//output.out", "w", stdout); }
#define MS(x, y) memset(x, y, sizeof(x))
#define ls o<<1
#define rs o<<1|1
typedef long long LL;
typedef unsigned long long UL;
typedef unsigned int UI;
template <class T1, class T2>inline void gmax(T1 &a, T2 b) { if (b > a)a = b; }
template <class T1, class T2>inline void gmin(T1 &a, T2 b) { if (b < a)a = b; }
const int N = 2020, M = 0, Z = 1e9 + 7, inf = 0x3f3f3f3f;
template <class T1, class T2>inline void gadd(T1 &a, T2 b) { a = (a + b) % Z; }
int casenum, casei;
int n, m;
int f[N][N];
struct PRE
{
short i, j, x, y;
}pre[N][N];
void init()
{
int cnt = 0;
MS(f, 63); f[1][1] = 1;
for (int i = 1; i <= 2000; ++i)
{
for (int j = 1; i + j <= 2000; ++j)if(f[i][j] <= 15)
{
//f[i * y][j * (x + y)]
for (int y = 1; i * y <= 2000; ++y)
{
for (int x = 1; j * (x + y) <= 2000; ++x)if (f[i][j] + f[x][y] <= 15)
{
++cnt;
int tmp = f[i][j] + f[x][y];
if (tmp < f[i * y][j * (x + y)])
{
f[i * y][j * (x + y)] = tmp;
pre[i * y][j * (x + y)] = { (short)i, (short)j, (short)x, (short)y };
}
}
}
//f[x * j][y * (i + j)]
for (int x = 1; x * j <= 2000; ++x)
{
for (int y = 1; y * (i + j) <= 2000; ++y)if (f[i][j] + f[x][y] <= 15)
{
++cnt;
int tmp = f[i][j] + f[x][y];
if (tmp < f[x * j][y * (i + j)])
{
f[x * j][y * (i + j)] = tmp;
pre[x * j][y * (i + j)] = { (short)x, (short)y, (short)i, (short)j };
}
}
}
}
}
//printf("Compute Time = %d\n", cnt);
}
int ID;
void print(int rt, int x, int y)
{
if (x == 1 && y == 1)return;
print(rt, pre[x][y].i, pre[x][y].j);
printf("%d %d\n", rt, ++ID);
print(ID, pre[x][y].x, pre[x][y].y);
}
int main()
{
init();
scanf("%d", &casenum);
for (casei = 1; casei <= casenum; ++casei)
{
scanf("%d", &m); ++m;
ID = 1;
bool flag = 0;
for (int i = 1; i < m; ++i)if (f[i][m - i] <= 15)
{
printf("%d\n", f[i][m - i]);
print(1, i, m - i);
flag = 1;
break;
}
if (!flag)puts("-1");
}
return 0;
}
/*
【trick&&吐槽】
1,csy还是套路深啊
2,状态太复杂的还是用下标表示得好
3,我们四重循环,实际复杂度却并不高,但是要用对称DP来实现无缝的值域覆盖
【题意】
我们最多使用15个节点,构造出一棵树,使得——这棵树中恰好有m个不同的集合{}
对于其中任意一个集合,其必须要是个独立集,即:其里面的任意两个点之间没有直接边相连。
【分析】
我们假设自己已经知道了树形态,思考如何求出独立集。
这个可以是2 ^ n * n枚举,当然最好的方法是树形DP。
这是一棵树,所以独立集的判定其实较为简单一点。
同时,独立集的转移也比较简单,我们只要考虑一个点取与不取两种情况。
于是,我们可以定义状态——
f[i][j]表示,如果我们选取根节点,会有i个独立集;如果我们不选取根节点,会有j个独立集,在这种条件下对应的最少节点数。
为什么定义出这么一个状态来呢?
因为,我们考虑独立集的计数时,是要求没有相邻点的。
于是,需要知道一个点选或不选对应的独立集个数。而这个点其实对应于一个根。
即:我们每次操作的时候必然基于一个点,就不妨把这个点设置为根。
我们知道了f[i][j]与f[x][y]这样两个树形态,我们考虑合并。
当然合并的对象是两个子树的根,合并之后的根可能是f[i][j]的根,也可能是f[x][y]的根。
假如合并之后的根是f[i][j]的根,与f[x][y]做合并,变成了f[i * y][j * (x + y)];
假如合并之后的根是f[x][y]的根,与f[i][j]做合并,变成了f[x * j][y * (i + j)];
初始状态是什么呢?f[1][1] = 1. 即我们把0个点选取的独立集也考虑,这样才使得合并计数准确。至于输入的m,使得m += 1即可。
【时间复杂度&&优化】
Compute Time = 206561
*/