5328. 【NOIP2017提高A组模拟8.22】世界线 bitset+拓补序合并

来源:互联网 发布:linux shutdown命令 编辑:程序博客网 时间:2024/06/05 01:55

题意:给你一个DAG,算每个点能到达的点数-已经连边的边数。
n<=6e4,m<=1e5
这数据范围很鬼畜啊。直接暴力拓补序合并GG,加个bitset也有点危险,然后我就学习了一种新姿势。。
每次只存500个点,类似于循环展开,那么时间复杂度就是O(nm/64)变为O(nm/500/64)(还有这种操作。。)
bitset中能到达的点设为1,否则为0.

#include<cstdio>#include<algorithm>#include<cstring>#include<bitset> #define fo(i,a,b) for(int i=a;i<=b;i++)#define fd(i,a,b) for(int i=a;i>=b;i--)using namespace std;const int N=3e5+5;bitset <505> a[N]; typedef long long ll;int n,m;int head[N],next[N],go[N],tot;int du[N],q[N],t,w;ll ans;inline void add(int x,int y){    go[++tot]=y;    next[tot]=head[x];    head[x]=tot;}int main(){    //freopen("worldline.in","r",stdin);    //freopen("worldline.out","w",stdout);    scanf("%d%d",&n,&m);    ans=-m;    fo(i,1,m)    {        int x,y;        scanf("%d%d",&x,&y);        add(x,y);        ++du[y];    }    fo(i,1,n)if (!du[i])q[++t]=i;    fo(x,1,t)    {        for(int i=head[q[x]];i;i=next[i])        {            int v=go[i];            if (!--du[v])q[++t]=v;        }    }    int l=1,r=500;    for(;l<=n;l=r+1,r+=500)    {        fo(i,1,n)a[i].reset();        fd(i,t,1)        {            for(int j=head[q[i]];j;j=next[j])            {                int v=go[j];a[q[i]]|=a[v];              }            ans+=a[q[i]].count();            if (q[i]>=l&&q[i]<=r)a[q[i]][q[i]-l]=1;        }    }    printf("%lld\n",ans);}
阅读全文
1 0
原创粉丝点击