BZOJ[3887][Usaco2015 Jan]Grass Cownoisseur Tarjan+拓扑排序

3 篇文章 0 订阅
2 篇文章 0 订阅

题目链接http://www.lydsy.com/JudgeOnline/problem.php?id=3887

Description

给一个有向图,然后选一条路径起点终点都为1的路径出来,有一次机会可以沿某条边逆方向走,问最多有多少个点可以被经过?(一个点在路径中无论出现多少正整数次对答案的贡献均为1)

Sample Input

7 10
1 2
3 1
2 5
2 4
3 7
3 5
3 6
6 5
7 2
4 7
Sample Output

6

边是可以重复的,则对于每个强连通分量,如果一个点可以被走到,则所有的点一定都可以被走到
如果我们没有逆着某条边走的机会,那么答案就是 1 所在的强连通分量
而现在我们有这个机会,如果将一条边from>to反置,就等同于将 1 ~to from ~ 1 连成了一个新环,环的大小就是1~ to from ~ 1 经过所有强连通分量的size
所以可以在Tarjan缩完点后按拓扑序分别求出 1 到每个点i和每个点 i 1的距离
最后枚举每一条边,计算一下将该边反置的答案就 OK

代码如下:

#include<cstdio>
#define N 100020
using namespace std;
char xB[1<<15],*xS=xB,*xTT=xB;
#define getc() (xS==xTT&&(xTT=(xS=xB)+fread(xB,1,1<<15,stdin),xS==xTT)?0:*xS++)
#define isd(c) (c>='0'&&c<='9')
inline int min(int a,int b){if(a<b) return a;return b;}
inline int max(int a,int b){if(a>b) return a;return b;}
inline int read(){
    register int x=0; char c;
    do {c=getc();} while(!isd(c));
    do x=(x<<3)+(x<<1)+c-'0',c=getc(); while(isd(c));
    return x;
}
struct Edge{
    int to,nex;
    Edge(int _=0,int __=0):to(_),nex(__){}
}nex[N],nex1[N],Nex[N];
int q[N];
bool b[N];
int fir[N],fir1[N],Fir[N],cd[N],cd1[N];
int f[N],f1[N];
int dfn[N],low[N],s[N],belong[N],size[N];
int n,m,x,y,v,top,top1,Top,scc_cnt,T,ans;
inline bool add(int x,int y,int &top,int fir[],Edge nex[]){
    nex[++top]=Edge(y,fir[x]);
    fir[x]=top;
}
void Tarjan(int x){
    dfn[x]=low[x]=++T;s[++top]=x;
    b[x]=true;
    register int i;
    for(i=Fir[x];i;i=Nex[i].nex){
        if(!dfn[Nex[i].to]){
            Tarjan(Nex[i].to);
            low[x]=min(low[x],low[Nex[i].to]);
        }
        else if(b[Nex[i].to]) low[x]=min(low[x],low[Nex[i].to]);
    }
    if(dfn[x]==low[x]){
        scc_cnt++;
        do{
            v=s[top--];
            belong[v]=scc_cnt;
            size[scc_cnt]++;
            b[v]=false;
        }while(v!=x);
    }
}
inline void Toposort(int fir[],Edge nex[],int cd[],int f[]){
    register int i,l,r;
    l=1;r=0;
    for(i=1;i<=scc_cnt;i++){
        if(!cd[i]) q[++r]=i;
        f[i]=-2147483647;
    }
    f[belong[1]]=size[belong[1]];
    while(l<=r){
        int x=q[l++];
        for(i=fir[x];i;i=nex[i].nex){
            f[nex[i].to]=max(f[x]+size[nex[i].to],f[nex[i].to]);
            cd[nex[i].to]--;
            if(!cd[nex[i].to]) q[++r]=nex[i].to;
        }
    }
}
main(){
    n=read();m=read();
    register int i,j;
    for(i=1;i<=m;i++){
        x=read();y=read();
        add(x,y,Top,Fir,Nex);
    }
    top=0;
    for(i=1;i<=n;i++)
        if(!dfn[i]){
            Tarjan(i);
            top=0;
        }
    top=0;
    for(i=1;i<=n;i++)
        for(j=Fir[i];j;j=Nex[j].nex){
            if(belong[i]==belong[Nex[j].to]) continue;
            add(belong[Nex[j].to],belong[i],top,fir,nex); cd[belong[i]]++;
            add(belong[i],belong[Nex[j].to],top1,fir1,nex1); cd1[belong[Nex[j].to]]++;
        }
    Toposort(fir,nex,cd,f);
    Toposort(fir1,nex1,cd1,f1);
    ans=size[belong[1]]<<1;
    for(i=1;i<=n;i++)
        for(j=Fir[i];j;j=Nex[j].nex){
            if(f1[belong[Nex[j].to]]>=0 && f[belong[i]]>=0)//-INF+-INF
            ans=max(ans,f1[belong[Nex[j].to]]+f[belong[i]]);
        }
    printf("%d",ans-size[belong[1]]);
return 0;
}

总结:
①-INF+-INF得正的
②register好快啊
③STL就是垃圾
④三位运算符贼慢
⑤cyk好神啊

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
好的,这是一道经典的单调栈问题。题目描述如下: 有 $n$ 个湖,第 $i$ 个湖有一个高度 $h_i$。现在要在这些湖之间挖一些沟渠,使得相邻的湖之间的高度差不超过 $d$。请问最少需要挖多少个沟渠。 这是一道单调栈的典型应用题。我们可以从左到右遍历湖的高度,同时使用一个单调栈来维护之前所有湖的高度。具体来说,我们维护一个单调递增的栈,栈中存储的是湖的下标。假设当前遍历到第 $i$ 个湖,我们需要在之前的湖中找到一个高度最接近 $h_i$ 且高度不超过 $h_i-d$ 的湖,然后从这个湖到第 $i$ 个湖之间挖一条沟渠。具体的实现可以参考下面的代码: ```c++ #include <cstdio> #include <stack> using namespace std; const int N = 100010; int n, d; int h[N]; stack<int> stk; int main() { scanf("%d%d", &n, &d); for (int i = 1; i <= n; i++) scanf("%d", &h[i]); int ans = 0; for (int i = 1; i <= n; i++) { while (!stk.empty() && h[stk.top()] <= h[i] - d) stk.pop(); if (!stk.empty()) ans++; stk.push(i); } printf("%d\n", ans); return 0; } ``` 这里的关键在于,当我们遍历到第 $i$ 个湖时,所有比 $h_i-d$ 小的湖都可以被舍弃,因为它们不可能成为第 $i$ 个湖的前驱。因此,我们可以不断地从栈顶弹出比 $h_i-d$ 小的湖,直到栈顶的湖高度大于 $h_i-d$,然后将 $i$ 入栈。这样,栈中存储的就是当前 $h_i$ 左边所有高度不超过 $h_i-d$ 的湖,栈顶元素就是最靠近 $h_i$ 且高度不超过 $h_i-d$ 的湖。如果栈不为空,说明找到了一个前驱湖,答案加一。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值