我又回来啦
第一次尝试使用markdown文本,可能编辑不好,见谅咕咕咕
(这个网站里有全部蓝书的题目,qq登录即可,good)
咳咳下面步入正题,关于这种题目,看到的第一眼应该能想到相当于动规的递推,但是要求边界如果直接dfs,30000的数据不知道你家的栈能不能受的住
所以这个题目提供了一个很好的思路,就是先求出拓扑序然后反过来一个一个求,这样可以保证求当前节点数据时(小于等于30000不用longlong)所用到的节点都以经算好了,就这么简单
就这么简单??
你好好考虑过动归方程了吗?
f(x)={x}并(所有f(y)的并,存在有向边(x,y))
为甚不是加??
因为有可能会有重复。比如3->4,2->4那直接加起来,4就会算两次
怎么破??
主角闪亮登场!!
状压
用N位二进制数,反之一个点到其他点只有能与不能
1就是能呗,2就是不能呗
实现:
- 数组实现(不够装逼)
- bitset!
一直不知道bitset怎么用
今天先码几个呗( 嘤嘤嘤 )
1:定义
bitset<大小>名称
bitset<大小>名称[数组大小]
2:运算
假设有bit[30001]
可以直接赋值bit[1][1]=1;
或者bit[1]="10011"; 注意是字符串,前面补0
就是第一个1位为1
可以直接bit[1]|bit[2] (全部按位或)
2019.12.15 20.14--------------------
3:复杂度
bitset 就是通过固定的优化,使得一个字节的八个比特能分别储存 8 位的 0/1 。
对于一个 4 字节的 int 变量,bitset 占用空间只是其1/32,计算一些信息时,所需时间也是其1/32。 --来源
赚啊滑稽
4:成员函数
.count()返回1的个数
其实还有很多,但最多用的(状压中)也就是这个了吧
——————————
下面也就是状压的常规操作了,也没啥好说的了。下面到了最快乐的
code time
//CH2101
#include<iostream>
#include<bitset>
#include<queue>
using namespace std;
#define N 30005
bitset<N> bit[N];
queue<int>q;
struct xx{
int to,nxt;
}e[N];int hd[N],num;
inline void add(int fm,int to){
e[++num].to=to;e[num].nxt=hd[fm];
hd[fm]=num;
}
int topo[N],d[N],cnt;
int n,m;
void tp(){
for(int i=1;i<=n;i++)
if(!d[i])q.push(i),topo[++cnt]=i;
while(!q.empty()){
int x=q.front();
q.pop();
for(int t=hd[x];t;t=e[t].nxt){
int y=e[t].to;
d[y]--;
if(!d[y])q.push(y),topo[++cnt]=y;
}
}
}
int main(){
cin>>n>>m;
for(int x,y,i=1;i<=m;i++){
cin>>x>>y;
add(x,y);
d[y]++;
}
tp();
for(int i=cnt;i;i--){ //由于这是按tp序来的,所有子节点都以经ok了
int x=topo[i];
bit[x][x]=1;
for(int t=hd[x];t;t=e[t].nxt){
bit[x]|=bit[e[t].to]; //可以直接按位或
}
}
for(int i=1;i<=n;i++) cout<<bit[i].count()<<endl;
// char c=getchar();
return 0;
}