【BZOJ】1512 [POI2006]Pro-Professor Szu 强联通分量+拓扑

80 篇文章 0 订阅
5 篇文章 0 订阅

题目传送门

以后不管打什么代码,一定要过心,不要因为这份代码打过很多遍就放松警惕:这题我因为读优打错了,WA了5发……心塞.jpg

这题的正解是tarjan+拓扑,题目要求 n 个点到主建筑的方案数,可以考虑主建筑到n个点的方案数,显然答案不变。

对于一个主建筑能到达的、节点数大于 1 <script type="math/tex" id="MathJax-Element-6">1</script>的强连通分量,显然第一问直接输出zawsze。这题还有一个情况,有的点可能有自环,直接当做上面的那个情况考虑即可。

剩下的情况直接在原图中找最长路即可,这个可以用拓扑实现。

p.s.对于第二、三问,主建筑不能走到主建筑……呵呵.png

附上AC代码:

#include <cstdio>
#include <cctype>
#include <algorithm>
#include <queue>
using namespace std;

const int N=1e6+10,inf=36501;
struct side{
    int to,nt;
}s[N],e[N];
int n,m,x,y,num,h[N];
int sk[N],d[N],t[N],size,top,sum,sy[N],sz[N];
int u[N],rd[N],f[N],put[N],len,ans;
queue <int> que;
bool b[N],bo[N];

inline char nc(){
    static char ch[100010],*p1=ch,*p2=ch;
    return p1==p2&&(p2=(p1=ch)+fread(ch,1,100010,stdin),p1==p2)?EOF:*p1++;
}

inline void read(int &a){
    static char c=nc();int f=1;
    for (;!isdigit(c);c=nc()) if (c=='-') f=-1;
    for (a=0;isdigit(c);a=a*10+c-'0',c=nc());
    return (void)(a*=f);
}

inline void so(int x){
    d[x]=t[x]=++size,b[x]=1,sk[++top]=x;
    for (int i=h[x]; i; i=s[i].nt)
        if (!d[s[i].to]) so(s[i].to),t[x]=min(t[x],t[s[i].to]);
        else if (b[s[i].to]) t[x]=min(t[x],d[s[i].to]);
    if (d[x]==t[x]){
        ++sum;
        while (1){
            int p=sk[top--];b[p]=0,sy[p]=sum;
            if (p==x) break;
        }
    }
    return;
}

int main(void){
    read(n),read(m),++n;
    for (int i=1; i<=m; ++i){
        read(x),read(y),s[++num]=(side){x,h[y]},h[y]=num;
        if (x==y) bo[x]=1;
    }
    for (int i=1; i<=n; ++i) if (!d[i]) so(i);
    for (int i=1; i<=n; ++i) ++sz[sy[i]];
    num=0;
    for (int i=1; i<=n; ++i)
        for (int j=h[i]; j; j=s[j].nt)
            if (sy[i]!=sy[s[j].to]){
                e[++num]=(side){sy[s[j].to],u[sy[i]]},u[sy[i]]=num;
                ++rd[sy[s[j].to]];
            }
    for (int i=1; i<=n; ++i) if (bo[i]) ++sz[sy[i]];
    que.push(sy[n]);
    if (sz[sy[n]]>1) f[sy[n]]=inf; else f[sy[n]]=1;
    while (!que.empty()){
        int p=que.front();que.pop();
        for (int i=u[p]; i; i=e[i].nt){
            f[e[i].to]+=f[p];
            if (f[e[i].to]>inf||(sz[e[i].to]>1&&f[e[i].to])) f[e[i].to]=inf;
            if ((--rd[e[i].to])==0) que.push(e[i].to);
        }
    }
    for (int i=1; i<n; ++i) ans=max(ans,f[sy[i]]);
    printf(ans==inf?"zawsze\n":"%d\n",ans);
    for (int i=1; i<n; ++i) if (f[sy[i]]==ans) put[++len]=i;
    printf("%d\n",len);
    for (int i=1; i<=len; ++i) printf("%d ",put[i]);
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值