题目

首先我要吐槽,这题目就是坑,给那么多无用的信息,我还以为要根据提示才能做出来呢!

算法1

暴力,傻傻地跟着提示,纯暴力\(40\)分,高斯消元\(60\)分。

算法2

DP!一个显然的东西是,这个矩阵有很多地方都是\(0\),所以我们枚举的许多排列都是无用的。

设\(f(i,set)\),其中\(i\)表示计算到排列的第\(i\)个元素,或者说是到矩阵的第\(i\)行,\(set\)是一个集合,表示前一行哪些数字还没选,可知\(set\)的大小为\(2k\)(这样我们才能DP嘛)。\(f\)的值表示当前计算到的行列式的值。

然后转移的时候我们要统计新产生的逆序对,进而判断是否要乘\(-1\)。

时间复杂度\(O(2^{2k}nk)\)。

算法3

直接DP,不要想什么矩阵。

假设我们DP到了第\(i\)位,显然有用的信息只有\(i-k \sim i-1\)这\(k\)个点的连通性。

然后暴力出所有的状态(要最小表示法,只有\(52\)种状态),搞出它们之间的转移,然后直接矩阵乘法即可。时间复杂度\(O(52^3 \log n)\)。

代码

#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#include <assert.h>
using namespace std; #ifdef debug
#define ep(...) fprintf(stderr, __VA_ARGS__)
#else
#define ep(...) assert(true)
#endif typedef long long i64; const int MAXK = 5;
const int MAXS = 60;
const int MOD = 65521;
const int LOGN = 60;
const int HASHSIZE = 1 << MAXK * 3; int cntS;
int k;
i64 n; struct MatrixB {
i64 A[MAXS][MAXS]; i64* operator [] (const int &x) {
return A[x];
}
}; struct MatrixA {
i64 A[MAXS]; i64 &operator [] (const int &x) {
return A[x];
}
}; void multi(MatrixB &A, MatrixB &B, MatrixB &C) {
for (int i = 0; i < cntS; i ++)
for (int j = 0; j < cntS; j ++) {
C[i][j] = 0;
for (int k = 0; k < cntS; k ++)
C[i][j] += A[i][k] * B[k][j];
C[i][j] %= MOD;
}
} void multi(MatrixA &A, MatrixB &B, MatrixA &C) {
for (int i = 0; i < cntS; i ++) {
C[i] = 0;
for (int j = 0; j < cntS; j ++)
C[i] += B[j][i] * A[j];
C[i] %= MOD;
}
} MatrixB B[LOGN]; struct Status {
int A[MAXK]; int &operator [] (const int &x) {
return A[x];
} int transform() {
int ret = 0;
for (int i = k - 1; i >= 0; i --) {
ret <<= 3;
ret |= A[i];
}
return ret;
} void show() {
#ifdef debug
for (int i = 0; i < k; i ++)
ep("%d ", A[i]);
ep("\n");
#endif
}
}; int hash[HASHSIZE]; #define test(s, i) (((s) >> (i)) & 1) Status transform(int x) {
Status ret;
for (int i = 0; i < k; i ++) {
ret[i] = x & 7;
x >>= 3;
}
return ret;
} int dfs(int f) {
Status cur = transform(f);
if (hash[f] == -1) {
hash[f] = cntS ++;
int cnt[MAXK];
fill(cnt, cnt + k, 0);
for (int i = 0; i < k; i ++)
cnt[cur[i]] ++;
for (int s = 0; s < 1 << k; s ++) {
int con = 1;
Status nxt = cur;
int in = -1; if (cnt[0] == 1 && test(s, 0) == 0) continue; for (int i = 0; i < k; i ++)
if (test(s, i)) {
con *= cnt[i];
if (in == -1) in = i;
else {
for (int j = 0; j < k; j ++)
if (nxt[j] == i) nxt[j] = in;
}
}
if (! con) continue; static int mapTo[MAXK];
fill(mapTo, mapTo + MAXK, -1);
int z = 0;
for (int i = 1; i < k; i ++)
if (mapTo[nxt[i]] == -1) {
mapTo[nxt[i]] = z ++;
}
for (int i = 0; i + 1 < k; i ++)
nxt[i] = mapTo[nxt[i + 1]];
nxt[k - 1] = in == -1 || mapTo[in] == -1 ? z : mapTo[in]; cur.show();
ep("%d %d\n", s, con);
nxt.show();
ep("--------------\n"); int h = nxt.transform();
int idx = dfs(h);
B[0][hash[f]][idx] += con;
B[0][hash[f]][idx] %= MOD;
}
}
return hash[f];
} MatrixA A, tA; void dfs2(int cur, Status s, int combination) {
if (cur == k) {
s.show();
ep("%d\n-----------\n", combination);
int idx = hash[s.transform()];
A[idx] += combination;
A[idx] %= MOD;
}
else {
int cnt[MAXK];
fill(cnt, cnt + cur + 1, 0);
for (int i = 0; i < cur; i ++)
cnt[s[i]] ++;
for (int S = 0; S < 1 << cur; S ++) {
int con = 1;
Status nxt = s;
nxt[cur] = -1;
for (int i = 0; i < cur; i ++)
if (test(S, i)) {
con *= cnt[i];
if (nxt[cur] == -1) nxt[cur] = i;
else {
for (int j = 0; j < cur; j ++)
if (nxt[j] == i) nxt[j] = nxt[cur];
}
}
if (! con) continue;
static int mapTo[MAXK];
int z = 0;
fill(mapTo, mapTo + cur, -1);
for (int i = 0; i < cur; i ++) {
if (mapTo[nxt[i]] == -1) {
mapTo[nxt[i]] = z ++;
}
nxt[i] = mapTo[nxt[i]];
} if (nxt[cur] == -1) {
int x = 0;
while (true) {
bool ok = true;
for (int i = 0; i < cur; i ++)
if (nxt[i] == x) {
ok = false;
break;
}
if (! ok) x ++;
else break;
}
nxt[cur] = x;
}
dfs2(cur + 1, nxt, combination * con);
}
}
} int main() {
#ifndef ONLINE_JUDGE
freopen("count.in", "r", stdin);
freopen("count.out", "w", stdout);
#endif scanf("%d%lld", &k, &n);
memset(hash, -1, sizeof hash);
dfs(0);
ep("find %d\n", cntS);
ep("-----------\n");
for (int i = 0; i < cntS; i ++) {
for (int j = 0; j < cntS; j ++)
ep("%d ", B[0][i][j]);
ep("\n");
}
ep("===================\n");
for (int i = 1; i < LOGN; i ++)
multi(B[i - 1], B[i - 1], B[i]); Status s;
dfs2(0, s, 1);
ep("-----------\n");
for (int i = 0; i < cntS; i ++)
ep("%d ", A[i]);
ep("\n");
ep("===================\n"); n -= k;
for (int i = 0; i < LOGN; i ++)
if (n >> i & 1) {
tA = A;
multi(tA, B[i], A);
} i64 ans = 0;
for (int i = 0; i < HASHSIZE; i ++)
if (hash[i] != -1) {
Status x = transform(i);
bool ok = true;
for (int j = 1; j < k; j ++)
if (x[j] != x[j - 1]) {
ok = false;
break;
}
if (ok) ans += A[hash[i]];
}
ans %= MOD;
printf("%d\n", (int) ans);
ep("%lld\n", ans); return 0;
}

NOI2007 生成树计数的更多相关文章

  1. BZOJ1494 [NOI2007]生成树计数

    题意 F.A.Qs Home Discuss ProblemSet Status Ranklist Contest 入门OJ ModifyUser  autoint Logout 捐赠本站 Probl ...

  2. [BZOJ1494][NOI2007]生成树计数 状压dp 并查集

    1494: [NOI2007]生成树计数 Time Limit: 5 Sec  Memory Limit: 64 MBSubmit: 793  Solved: 451[Submit][Status][ ...

  3. [NOI2007]生成树计数环形版

    NOI2007这道题人类进化更完全之后出现了新的做法 毕姥爷题解: 于是毕姥爷出了一道环形版的这题(test0814),让我们写这个做法 环形的情况下,k=5的时候是162阶递推. 求这个递推可以用B ...

  4. [BZOJ1494]生成树计数

    [BZOJ1494] [NOI2007]生成树计数 Description 最近,小栋在无向连通图的生成树个数计算方面有了惊人的进展,他发现:·n个结点的环的生成树个数为n.·n个结点的完全图的生成树 ...

  5. 【BZOJ1494】【NOI2007】生成树计数(动态规划,矩阵快速幂)

    [BZOJ1494][NOI2007]生成树计数(动态规划,矩阵快速幂) 题面 Description 最近,小栋在无向连通图的生成树个数计算方面有了惊人的进展,他发现: ·n个结点的环的生成树个数为 ...

  6. 【BZOJ1002】【FJOI2007】轮状病毒(生成树计数)

    1002: [FJOI2007]轮状病毒 Time Limit: 1 Sec  Memory Limit: 162 MBSubmit: 1766  Solved: 946[Submit][Status ...

  7. SPOJ 104 HIGH - Highways 生成树计数

    题目链接:https://vjudge.net/problem/SPOJ-HIGH 解法: 生成树计数 1.构造 基尔霍夫矩阵(又叫拉普拉斯矩阵) n阶矩阵 若u.v之间有边相连 C[u][v]=C[ ...

  8. Luogu P5296 [北京省选集训2019]生成树计数

    Luogu P5296 [北京省选集训2019]生成树计数 题目链接 题目大意:给定每条边的边权.一颗生成树的权值为边权和的\(k\)次方.求出所有生成树的权值和. 我们列出答案的式子: 设\(E\) ...

  9. Loj 2320.「清华集训 2017」生成树计数

    Loj 2320.「清华集训 2017」生成树计数 题目描述 在一个 \(s\) 个点的图中,存在 \(s-n\) 条边,使图中形成了 \(n\) 个连通块,第 \(i\) 个连通块中有 \(a_i\ ...

随机推荐

  1. IT资源专业搜索-www.easysoo.cn

    创始人:samrthhl 时间:2015-11-8 关于易搜 易搜(www.easysoo.cn)是一个面向IT开发从业人员的专业资源搜索站点,它将全球的知名博客论坛.专业IT行业站点.知名咨询机构和 ...

  2. Python之路:迭代器和yield生成器

    一.迭代器 对于Python 列表的 for 循环,他的内部原理:查看下一个元素是否存在,如果存在,则取出,如果不存在,则报异常 StopIteration.(python内部对异常已处理) 使用迭代 ...

  3. 解决Jetty Maven Plugin:Please initialize the log4j system properly(转)

    解决Jetty Maven Plugin:Please initialize the log4j system properly.Jetty Maven Plugin环境: <plugin> ...

  4. BZOJ 1756: Vijos1083 小白逛公园

    题目 1756: Vijos1083 小白逛公园 Time Limit: 10 Sec  Memory Limit: 64 MBSubmit: 856  Solved: 264[Submit][Sta ...

  5. ASP.NET 内置对象涉略

    一.ASP.NET中内置的常用对象的介绍 本文列举了ASP.NET 的八个内置对象,其中前五个是比较常用的. 1.Response Response 对象用于从服务器向用户发送输出的结果. Write ...

  6. BNU Invading system

    http://www.bnuoj.com/bnuoj/problem_show.php?pid=29364 这个题被坑了. 题意:密码就是那些数字里面的数,转换成二进制后1最少的那个数,当1的个数相同 ...

  7. z-index要同级比较,absolute包含块外有overflow-hidden

    z-index只能在position属性值为relative或absolute或fixed的元素上有效. z-index只决定同一父元素中的同级子元素的堆叠顺序. position:absolute ...

  8. 异常语句:try(尝试)-catch(抓取)-finally 跳转语句:break

    跳转语句: 1.break:跳出的意思,如果在循环语句中使用则是跳出循环2.default,--默认语句通常与 switch case 配合使用3.continue--跳过一个,继续下一个继续retu ...

  9. C# lazy<T>的用法

    .NET 4.0中加入了lazy<T>(懒对象),其实叫懒对象感觉不对,更应该叫延迟对象加载. 正如我们所知,对象的加载是需要消耗时间的,特别是对于大对象来说消耗的时间更多.lazy可以实 ...

  10. load、save方法、spark sql的几种数据源

    load.save方法的用法          DataFrame usersDF = sqlContext.read().load("hdfs://spark1:9000/users.pa ...