欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

世界线 【NOIP2017提高A组模拟8.22】

程序员文章站 2023-12-24 22:09:15
...

题目

世界线 【NOIP2017提高A组模拟8.22】

输入

世界线 【NOIP2017提高A组模拟8.22】

输出

世界线 【NOIP2017提高A组模拟8.22】

Sample Input

5 5
1 2
1 3
2 3
3 4
4 5

Sample Output

5

数据范围

世界线 【NOIP2017提高A组模拟8.22】


思路

当时我还没看到是有向边,然后觉得并查集搞搞就好了。
后来发现后,琢磨了一下,发现,如果一点x能到间接到另一个点y,那么点x就得向y连一条边。然而dfs后却难以判重,gg


解法

竟然是用bitset这种东西,这题目真是。。。。
用bitset,把每一个点用一个二进制数来表示其能到达的点。例如:如果x能到达2好点,那么bit[x]里第2位改成1。
然而这样会爆炸空间。
那么分两次做,第一次处理小于n/2的点,第二次处理大于n/2的点。


代码

#include<cstdlib>
#include<algorithm>
#include<cstring>
#include<cstdio>
#include<cmath>
#include<bitset>
#define fo(i,a,b) for(int i=a;i<=b;i++)

using namespace std;

const int maxn=6e4+50,maxm=1e5+5;
bitset<maxn/2> Bit[maxn];
int n,m,num,dad[maxn],go[maxm],fre[maxm],next[maxm],mid,bo,ans;
bool bz[maxn];

int read()
{
    int x=0; char c;
    for(c=getchar();c<'0'||c>'9';c=getchar());
    for(c;c>='0'&&c<='9';c=getchar()) x=x*10+c-48;
    return x;
}
void add(int x,int y)
{
    go[++num]=y;
    next[num]=fre[x];
    fre[x]=num;
}
void dfs(int x)
{
    if (!bo) {
        if (x<=mid) Bit[x].set(x);
    }
    else {
        if (x>mid) Bit[x].set(x-mid);
    }
    bz[x]=true;
    for(int i=fre[x];i;i=next[i]){
        if(!bz[go[i]]) dfs(go[i]);
        Bit[x]|=Bit[go[i]];
    }
}
int main()
{
    freopen("worldline.in","r",stdin);
    freopen("worldline.out","w",stdout);
    n=read(); m=read();
    fo(i,1,m){
        int x=read(),y=read();
        dad[y]=x;
        add(x,y);
    }
    mid=n/2;
    fo(i,1,n)
    if (!dad[i]) dfs(i);
    fo(i,1,n) {
        ans+=Bit[i].count();
        Bit[i].reset();
    }
    memset(bz,0,sizeof(bz));
    bo=1;
    fo(i,1,n)
    if (!dad[i]) dfs(i);
    fo(i,1,n) ans+=Bit[i].count();
    printf("%d\n",ans-m-n);
}

世界线 【NOIP2017提高A组模拟8.22】

上一篇:

下一篇: