SP4882 DAGCNT2 - Counting in a DAG
Posted notscience
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了SP4882 DAGCNT2 - Counting in a DAG相关的知识,希望对你有一定的参考价值。
题意:统计DAG上每一个点可以到达的所有节点的权值和(包括自己)。
思路如下:根据题意可以得到这样的关系:设(rec(x))为(x)点可达点的集合,假设存在一条有向边((x,y)),有(rec(x)=rec(x)cup rec(y))。显然集合(rec(x))的初始状态为({x})(可以抵达自己)。那么反向建图之后作一个拓扑排序,根据这个式子去作统计即可。
反向建图并且做拓扑排序是为了保证无后效性。
当然还有一个问题:我们如何记录这个集合?
这个时候我们可以运用STL的bitset来完成这个任务。
bitset的一点点基础使用方法:
bitset的每一位只有(01)一个值,空间占用非常低(低于布尔类型),它支持随机访问,像(f[1])这样子可以直接访问其中的一个位。
同时也支持位运算,可以直接作与(交集)、或(并集)、异或等运算。
记得选C++14!
#include<cstdio>
#include<queue>
#include<cstring>
#include<algorithm>
#include<bitset>
using namespace std;
struct data
{
int to,next;
}edge[500005];
int T,n,m,u,v,val[20005],cnt,inDegree[20005],order[20005],head[20005];
bool vis[20005];
bitset<20005> rec[20005];
void add(int u,int v)
{
edge[++cnt].to=v;
edge[cnt].next=head[u];
head[u]=cnt;
}
int main()
{
scanf("%d",&T);
while (T>0)
{
//记得重置数组
T--;
memset(head,0,sizeof(head));
memset(edge,0,sizeof(edge));
scanf("%d%d",&n,&m);
for (int i=1;i<=n;i++)
{
scanf("%d",&val[i]);
rec[i][i]=1;
}
cnt=0;
for (int i=1;i<=m;i++)
{
scanf("%d%d",&u,&v);
add(v,u);//反向建图,便于统计
inDegree[u]++;
}
cnt=0;
queue<int> que;
for (int i=1;i<=n;i++)
if (!inDegree[i])
que.push(i);
while (!que.empty())
{
u=que.front();
que.pop();
order[++cnt]=u;
for (int i=head[u];i;i=edge[i].next)
{
v=edge[i].to;
inDegree[v]--;
if (!inDegree[v]) que.push(v);
}
}//拓扑排序
for (int i=1;i<=n;i++)
{
int u=order[i];
for (int j=head[u];j;j=edge[j].next)
rec[edge[j].to]|=rec[u];//求并集
}
for (int i=1;i<=n;i++)
{
int ans=0;
for (int j=1;j<=n;j++)
if (rec[i][j]) ans+=val[j];//统计
printf("%d ",ans);
rec[i].reset();
}
printf("
");
}
return 0;
}
以上是关于SP4882 DAGCNT2 - Counting in a DAG的主要内容,如果未能解决你的问题,请参考以下文章
SP34096 DIVCNTK - Counting Divisors (general) min_25筛
hdu 4882 ZCC Loves Codefires(贪心)