题目大意:

有一张n个点,m条边的有向图,有多少个子图(选定一个边集)是没有环的。答案对1e9+7取模。( n≤17 n ≤ 17 n≤17)

思路:

话说这个题目作为NOIP的模拟题有点过了,毕竟 n≤17 n ≤ 17 n≤17的方法不是太好想(一定我是太菜了)。
先考虑 n≤10 n ≤ 10 n≤10的方法,构造一个dag图,一个好的方法就是可以根据拓扑序来分层,一个图的拓扑序一定是唯一的,所以我们可以一层一层地添加点集进去。将一开始入度为0的点放第一层,将这些点去掉,剩下出现的新的入度为0的点为第二层…以此类推,用f[i][j]表示总共选的点集和最后一层的点集就好了。注意转移的时候要满足新一层的拓扑序要全部都在原来的后面,即一定要满足有边连向上一层的点。

/*=========================* Author : ylsoi* Problem : obelisk* Algorithm : DP* Time : 2018.5.23* =======================*/
#include<iostream>
#include<algorithm>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<climits>
#include<vector>
using namespace std;
void File(){freopen("obelisk.in","r",stdin);freopen("obelisk.out","w",stdout);
}
template<typename T>bool chkmax(T &_,T __){return _<__ ? (_=__,1) : 0;}
template<typename T>bool chkmin(T &_,T __){return _>__ ? (_=__,1) : 0;}
#define REP(i,a,b) for(register int i=a;i<=b;++i)
#define DREP(i,a,b) for(register int i=a;i>=b;--i)
#define MREP(i,x) for(register int i=beg[x];i;i=E[i].last)
#define mem(a) memset(a,0,sizeof(a))
#define ll long long
#define inf INT_MAX
const int maxn=17+10;
const ll mod=1e9+7;
int n,m;
vector<int>from[maxn];
ll dp[(1<<10)+10][(1<<10)+10],all,ans;
ll qpow(ll x,int b){ll base=x,ret=1ll;while(b){if(b&1)ret=ret*base%mod;base=base*base%mod;b>>=1;}return ret;
}
void add(ll &_,ll __){_=((_+__)%mod+mod)%mod;}
void mul(ll &_,ll __){_=(_*__%mod+mod)%mod;}
bool in(int x,int S){return (1<<(x-1))&S;}
void out(int x){if(!x)return;out(x/2);printf("%d",x%2);
}
int main(){File();scanf("%d%d",&n,&m);REP(i,1,m){int u,v;scanf("%d%d",&u,&v);from[v].push_back(u);}all=(1<<n)-1;REP(i,1,all)dp[i][i]=1ll;REP(i,1,all){for(int j=i;j;j=(j-1)&i){if(!dp[i][j])continue;if(i==all)add(ans,dp[i][j]);int left=all-i;for(int k=left;k;k=(k-1)&left){bool flag=true;ll mul1=1ll,mul2=1ll;REP(v,1,n){if(!in(v,k))continue;bool flag1=false;int size=from[v].size()-1,cnt1=0,cnt2=0;REP(p,0,size){int u=from[v][p];if(in(u,i) && !in(u,j))++cnt1;if(in(u,j)){flag1=true;++cnt2;}}if(!flag1){flag=false;break;}mul(mul1,qpow(2,cnt1));mul(mul2,qpow(2,cnt2)-1);}if(!flag)continue;add(dp[i|k][k],dp[i][j]*mul1%mod*mul2%mod);}}}printf("%lld\n",ans);return 0;
}

好像这个方法就已经很不错了,但是并不满足我们的需要,考虑怎么把第二维给去掉。设f[i]表示选择了点集i的方案总数,自然是要一层一层地转移(要不然就会出错),但是依然按照拓扑序来分层地话就会难以实现,因为没有记录上一层状态的缘故,新添加的点不一定是拓扑序中最后面的。那么我们便按照出度为0的规则来添加点集,即出度为0的点是在新添加的这一层的。
设集合 i i i为当前的状态,枚举一个i" role="presentation">iii的补集的子集 j j j,两个集合的并集i|j" role="presentation">i|ji|ji|j中,有前面定义的最后一层 k k k,因为j" role="presentation">jjj是刚刚添加进去的且只有从后面来的边,所以必有 j⊆k j ⊆ k j\subseteq k。对于 i|j i | j i|j这个集合的答案,一定是等于所有的 k k k的状态的和,显然地,如果用这种方法计数,每一个k" role="presentation">kkk都会被添加进去多次,就是每一个 k k k的子集j" role="presentation">jjj都会代表 k k k一次,所以我们在相加的时候多乘以一个容斥系数(−1)size(j)+1" role="presentation">(−1)size(j)+1(−1)size(j)+1(-1)^{size(j)+1}即可,这样就可以保证每一个 k k k都只会被计算一次。
时间复杂度O(3nm)" role="presentation">O(3nm)O(3nm)O(3^nm),好像还是不够快,再优化我就不会了。。。

/*=========================* Author : ylsoi* Problem : obelisk* Algorithm : dp* Time : 2018.5.23* ======================*/
#include<iostream>
#include<algorithm>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<climits>
#include<vector>
using namespace std;
void File(){freopen("obelisk_better.in","r",stdin);freopen("obelisk_better.out","w",stdout);
}
#define REP(i,a,b) for(register int i=a;i<=b;++i)
#define DREP(i,a,b) for(register int i=a;i>=b;--i)
#define MREP(i,x) for(register int i=beg[x];i;i=E[i].last)
#define mem(a) memset(a,0,sizeof(a))
#define ll long long
#define inf INT_MAX
const int maxn=17+5;
const ll mod=1e9+7;
int n,m,all,c[(1<<17)+10][maxn];
bool dis[maxn][maxn];
ll dp[(1<<17)+10];
void add(ll &_,ll __){_=((_+__)%mod+mod)%mod;}
void mul(ll &_,ll __){_=(_*__%mod+mod)%mod;}
ll qpow(ll x,int b){ll base=x,ret=1ll;while(b){if(b&1)mul(ret,base);mul(base,base);b>>=1;}return ret;
}
bool in(int x,int S){return (1<<(x-1))&S;}
void init(){scanf("%d%d",&n,&m);all=(1<<n)-1;REP(i,1,m){int u,v;scanf("%d%d",&u,&v);dis[u][v]=1;}REP(i,1,all){REP(v,1,n){if(!in(v,i))continue;REP(u,1,n)c[i][u]+=dis[u][v];}}
}
void work(){dp[0]=1ll;int siz,cnt;REP(i,0,all){if(!dp[i])continue;int left=all-i;for(register int j=left;j;j=(j-1)&left){siz=__builtin_popcount(j),cnt=0;REP(u,1,n)if(in(u,i))cnt+=c[j][u];add(dp[i|j],dp[i]*qpow(2,cnt)%mod*qpow(-1,siz+1)%mod);}}printf("%lld\n",dp[all]);
}
int main(){File();init();work();return 0;
}

[bzoj]noip十连测欧贝里斯克的巨神兵(obelisk)——dag图DP,状态压缩相关推荐

  1. bzoj 1179 抢掠计划atm (缩点+有向无环图DP)

    bzoj 1179 抢掠计划atm (缩点+有向无环图DP) 手动博客搬家: 本文发表于20170716 10:58:18, 原地址https://blog.csdn.net/suncongbo/ar ...

  2. NOIP十连测 涂色游戏

    这是一道玄学组合数和神仙思路... 题目大意:给出一个n*m的网格,每个格子里只能涂一种颜色,一共有p中颜色,要求任意相邻两列都出现了 至少q种颜色的方案数. n≤100,m≤,q≤p≤100. 看这 ...

  3. 【BZOJ - 3036】绿豆蛙的归宿(概率DAG图dp,拓扑排序,概率dp,期望的线性性)

    题干: 随着新版百度空间的下线,Blog宠物绿豆蛙完成了它的使命,去寻找它新的归宿. 给出一个有向无环的连通图,起点为1终点为N,每条边都有一个长度.绿豆蛙从起点出发,走向终点. 到达每一个顶点时,如 ...

  4. 【bzoj 十连测】[noip2016十连测第三场]Problem C: 序列(静态主席树)

    Problem C: [noip2016十连测第三场]序列 Time Limit: 10 Sec  Memory Limit: 256 MB Submit: 78  Solved: 32 [Submi ...

  5. bzoj 5216 [Lydsy2017省队十连测]公路建设 线段树维护 最小生成树

    [Lydsy2017省队十连测]公路建设 Time Limit: 20 Sec  Memory Limit: 512 MB Submit: 93  Solved: 53 [Submit][Status ...

  6. winpcap基本原理及常见应用_碳十四测年的基本原理和常见应用谬误

    如果说地层学原理是18世纪地质学给19世纪考古学的重要礼物,那么放射性碳定年法就是20世纪考古学最重要的进步之一.这两种方法都对年代判定做出了重要的贡献,因为若是没有某种类型的年代测定,我们就不可能建 ...

  7. 第十二周项目4-利用遍历思想求解图问题(6-7)

    /* Copyright (c)2015,烟台大学计算机与控制工程学院 All rights reserved. 文件名称:第十二周项目4-利用遍历思想求解图问题(6-7) 作 者:佟兴锋 完成日期: ...

  8. iOS 11开发教程(二十)iOS11应用视图美化按钮之设置按钮的状态

    iOS 11开发教程(二十)iOS11应用视图美化按钮之设置按钮的状态 在示例2-2中,设置按钮的标题和颜色时,需要对按钮的状态进行设置,表示按钮在某一状态下的标题和标题颜色是什么样子.例如,UICo ...

  9. 【信息系统项目管理师】第十四章 文档与配置管理思维导图

    [信息系统项目管理师]第十四章 文档与配置管理思维导图 文档管理和配置管理是第十四章总两大知识板块. 文档管理 信息系统中的文档按质量分级可以分为1-4级分别是最低限度文档,内部文档,工作文档,正式文 ...

最新文章

  1. 转录组测序技术和结果解读(二)——文库构建和测序策略
  2. 成功搞定了在Windows7中安装SAP了
  3. 12 个最佳的免费网络监控工具--转载
  4. MS SQL入门基础:更改数据库
  5. Linq之Lambda进阶
  6. 368.最大整数子集
  7. 人脸识别相关数据集介绍
  8. WPS中设置一级、二级、三级标题以及正文和目录
  9. QQ坦白说查看好友方法攻略
  10. NVIDIA助力风暴英雄黄金世俱杯Ballistix强势夺冠
  11. Linux系统备份及迁移到新硬盘
  12. FcaNet: Frequency Channel Attention Networks
  13. 华三交换机配置vrrp_华三交换机 VRRP V7版本
  14. duplicate designator is not allowedC/C++(2906)
  15. 域名是干啥用的?企业自己都记不住的域名还能发挥作用吗?
  16. 面向对象程序设计php,php面向对象的程序设计
  17. python与r语言处理excel数据_R语言 | 读写txt、csv、excel文件
  18. eclips报错如下:
  19. CRMEB知识付费系统安装搭建【常见问题-14.安装无法修改写入权限】
  20. [读书笔记]《深度探索C++对象模型》

热门文章

  1. 使用Vue做一个自定义的日历小控件
  2. PMM 监控 MySQL
  3. 苹果无法连接至设备_手机资讯:二手 iPhone 无法下载 App 应如何解决
  4. 项目管理 : 冲突管理
  5. Linux之shell编程
  6. 职业教育标准教材·计算机组装与维修,中等职业教育计算机专业系列教材:计算机组装与维护...
  7. 【JZOJ 省选模拟】迷宫(maze)
  8. 修改Android 自带输入法(LatinIME)空格键的显示
  9. js/javascript获取数组长度
  10. python代码雨在桌面实现_今天七夕节,外面下着大雨,用Python的tkinter做一个下爱心雨的特效,发给妹子...