题意:

  一个有n个数的排列,给你一些位置上数字的大小关系。求合法的排列有多少种。

思路:

  数字的大小关系可以看做是一条有向边,这样以每个位置当点,就可以把整个排列当做一张有向图。而且题目保证有解,所以只一张有向无环图。这样子,我们就可以把排列计数的问题转化为一个图的拓扑排序计数问题。

  拓扑排序的做法可以参见ZJU1346

  因为题目中点的数量比较多,所以无法直接用状压DP。 但是题目中的边数较少,所以不是联通的,而一个连通块的点不超过21个,而且不同连通块之间可以看做相互独立的。所以我们可以对于每个连通块分别求解,然后利用排列组合的知识,把他们组合起来。这样就可以得到最终解了。

代码:

 #include <iostream>
#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <cmath>
#include <algorithm>
#include <string>
#include <queue>
#include <stack>
#include <vector>
#include <map>
#include <set>
#include <functional>
#include <time.h> using namespace std; typedef __int64 ll; const int INF = <<;
const int MAXM = ;
const int MAXN = ;
const ll MOD = (ll) 1e9+; ll dp[<<MAXM]; //
ll combine[MAXN][MAXN]; //组合数
int Matrix[MAXN][MAXN]; //邻接矩阵,1表示正向边,-1表示反向边
int tmp[MAXM], pre[MAXM], num; //求每个连通块的拓扑序用
bool vis[MAXN]; //求连通块用
int n, m, tot;
ll ans; void dfs(int u) {
int u2 = num; //当前点的标号
tmp[num++] = u; //把这个点加入集合
vis[u] = true; int v;
for (int i = ; i <= n; i++) if (Matrix[u][i]) { //如果有边
if (!vis[i]) dfs(i); //如果没找过先找后继
if (==Matrix[u][i]) {
for (v = ; v < num; v++) if (tmp[v]==i) {
pre[v] |= (<<u2); //把这个点加入后继的前驱集合
}
}
}
} ll calc() { //计算某个连通块的拓扑数量
memset(dp, , sizeof(dp));
dp[] = ; for (int S = ; S < (<<num); S++) if (dp[S]>)
for (int i = ; i < num; i++) if (((S&pre[i])==pre[i]) && !(S&(<<i))) {
dp[S|(<<i)] = (dp[S|(<<i)]+dp[S])%MOD;
} return dp[(<<num)-];
} void solve() {
memset(vis, false, sizeof(vis));
ans = ;
tot = n;
for (int i = ; i <= n; i++) if (!vis[i]) { //搜连通块
memset(pre, , sizeof(pre)); num = ;
dfs(i);
if (num<) //只有一个或者两个点的情况,拓扑序时确定的
ans = (((num*combine[tot][num])%MOD)*ans)%MOD;
else
ans = (((calc()*combine[tot][num])%MOD)*ans)%MOD; tot -= num;
} printf("%I64d\n", ans);
} int main() {
#ifdef Phantom01
freopen("HDU4917.in", "r", stdin);
// freopen("HDU4917.out", "w", stdout);
#endif //Phantom01 for (int i = ; i < MAXN; i++) { //预处理组合数(杨辉三角)
combine[i][] = ;
for (int j = ; j <= i; j++)
combine[i][j] = (combine[i-][j-] + combine[i-][j])%MOD;
} while (scanf("%d%d", &n, &m)!=EOF) {
memset(Matrix, , sizeof(Matrix));
int u, v;
for (int i = ; i < m; i++) {
scanf("%d%d", &u, &v);
Matrix[u][v] = ;
Matrix[v][u] = -;
}
solve();
} return ;
}

HDU4917

  写的时候,莫名其妙的wa了一发,要数据来对拍才发现是中间结果溢出了……所以,小心的调整运算顺序和适当的加括号很有必要。

HDU 4917 Permutation 拓扑排序的计数的更多相关文章

  1. HDU.2647 Reward(拓扑排序 TopSort)

    HDU.2647 Reward(拓扑排序 TopSort) 题意分析 裸的拓扑排序 详解请移步 算法学习 拓扑排序(TopSort) 这道题有一点变化是要求计算最后的金钱数.最少金钱值是888,最少的 ...

  2. ACM: hdu 2647 Reward -拓扑排序

    hdu 2647 Reward Time Limit:1000MS     Memory Limit:32768KB     64bit IO Format:%I64d & %I64u Des ...

  3. HDU 2647 Reward (拓扑排序)

    题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=2647 题意是给你n点m条有向边,叶子点(出度为0)上的值为888,父亲点为888+1,依次计算... ...

  4. hdu 5438 Ponds 拓扑排序

    Ponds Time Limit: 1 Sec Memory Limit: 256 MB 题目连接 http://acm.hdu.edu.cn/contests/contest_showproblem ...

  5. CF798E. Mike and code of a permutation [拓扑排序 线段树]

    CF798E. Mike and code of a permutation 题意: 排列p,编码了一个序列a.对于每个i,找到第一个\(p_j > p_i\)并且未被标记的j,标记这个j并\( ...

  6. 【NOIP2017】逛公园(最短路图,拓扑排序,计数DP)

    题意: 策策同学特别喜欢逛公园. 公园可以看成一张 N 个点 M 条边构成的有向图,且没有自环和重边.其中 1 号点是公园的入口, N 号点是公园的出口,每条边有一个非负权值,代表策策经过这条边所要花 ...

  7. hdu 4857 逃生 拓扑排序+PQ,剥层分析

    pid=4857">hdu4857 逃生 题目是求拓扑排序,但不是依照字典序最小输出,而是要使较小的数排在最前面. 一開始的错误思路:给每一个点确定一个优先级(该点所能到达的最小的点) ...

  8. HDU 4917 Permutation

    意甲冠军: 序列p1.p2.p3--pn由1.2.3--n这些数字  现在给出一些条件pi<pj  部条件的排列的个数 思路: 非常easy想到用一条有向的线连接全部的pi和pj  那么就构成了 ...

  9. HDU 1285 经典拓扑排序入门题

    确定比赛名次 Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Others)Total Subm ...

随机推荐

  1. 三级联动(ajax同步)

    html <div id="frame"></div> js $(function(){ //拼接省市区下拉框 var str = `<select ...

  2. <%=%>、<%%>、<%@%>、<%#%>的区别

    1.<%= %> 里面放变量名,获取后台的变量值,直接输入变量到页面上,里面放的变量名,未经过encode eg: 后台: seession["ab"]=ab; 前台: ...

  3. Hibernate框架学习(一)——入门

    一.框架是什么 1.框架是用来提高开发效率的 2.封装好了一些功能,我们需要使用这些功能时,调用即可,不需要手动实现 3.框架可以理解成一个半成品的项目,只要懂得如何驾驭这些功能即可 二.hibern ...

  4. .startsWith和endsWith的使用方法与说明

    a.startsWith(b) --判断字符串a,是不是以字符串b开头 a.endsWith(b) --判断字符串a,是不是以字符串b结尾

  5. oracle查询之null值转化

    函数coalesce(c1,c2,c3......cn);返回第一个不为null的值

  6. js 监听ios手机键盘弹起和收起的事件

    document.body.addEventListener('focusin', () => { //软键盘弹起事件 console.log("键盘弹起") }) docu ...

  7. Spring Framework 开发参考手册中文(在线HTML)

    https://blog.csdn.net/zfrong/article/details/3971722

  8. iptables 配置端口及转发

    iptables端口转发指令:iptables -t nat -A PREROUTING -p tcp --dport 443 -j REDIRECT --to-port 8443 iptables配 ...

  9. 【BZOJ 1293】[SCOI2009]生日礼物

    [链接] 我是链接,点我呀:) [题意] 在这里输入题意 [题解] 显然的滑动窗口题. (尺取法 如果l..i这一段已经有k种珍珠了. 那么就尝试把l++; (即把l这个影响尝试去掉一下 如果不足k种 ...

  10. 尼克的任务 dp 洛谷1280

    蒟蒻表示老久没看过dp题目了,,挺水的一道dp题目都没想出来,,, 首先设dp[i]表示从开始到i时间的最大空闲时间,用vector to[x] 表示从x点开始的任务结束时间,cnt[x]表示从x开始 ...