爬山【NOIP2016提高A组模拟9.10】

题目

国家一级爬山运动员h10今天获得了一张有着密密麻麻标记的地图,在好奇心的驱使下,他又踏上了去爬山的路。
对于爬山,h10有一个原则,那就是不走回头路,于是他把地图上的所有边都标记成了有向边。他决定从点S出发,每到达一个新的节点他就可以获得一定的成就值。同时h10又是一个很珍惜时间的运动员,他不希望这次爬山的成就值白白浪费,所以最后他一定要在一个存档点停下,保存自己的成就值。
请你计算出在此次爬山运动中h10能够得到的最大成就值。保证h10能走到存档点。

样例输入:
第一行两个整数 N,M,表示点数和边数。
接下来 M 行,每行两个整数 u,v,表示u到v有一条有向边(没有自环)。
第 M+2 行 N 个正整数,表示每个点的成就值。
接下来一行两个整数 S,p,表示出发点和存档点个数。
下面一行 p 个整数,表示存档点。
5 7
5 1
3 1
2 5
3 5
4 3
4 2
4 5
7 6 3 2 2
4 3
1 5 2

样例输出:
一个正整数,表示最大成就值。
17

数据范围:
对于 30% 的数据, N,M≤1000,并且地图为有向无环图。
对于 100% 的数据, N,M≤500000。(数据有梯度,注意答案的大小)


剖解题目

给一个有向有环图,每个点有个贡献,第一次到达此点可以获得这个贡献,问从一点出发,到达一些特定点中,能够获得最大的贡献是多少。


思路

没有环的很好处理,一遍最短路即可。
至于环,很容易想到缩点,将一个环缩为一点,毕竟以前打过,在一遍最短路即可。
然而,此题tarjan却爆栈!需要手工栈,然而我并没有打过手工栈,所以没有打,就GG了几个点。


解法

如上。


代码

#include
#include
#include
#include
#include
#define fo(i,a,b) for(int i=a;i<=b;i++)
#define ll long long

using namespace std;

const int maxn=1e5*5+2,mo=1e6+5;
int fre[maxn],next[maxn],go[maxn],a[maxn*2][3],t[maxn],dfn[maxn],low[maxn];
int now[maxn],hg[mo],keep[maxn],sta[maxn];
int n,m,S,p,num,k,top,tt,ttop;
bool bz[maxn*3],sbz[maxn];
ll ans,pro[maxn*3],dis[maxn*3];
stack<int>st;

void add(int x,int y)
{
    go[++num]=y;
    next[num]=fre[x];
    fre[x]=num;
}
void tarjan(int x)
{
    dfn[x]=++k; low[x]=k;
       sbz[x]=true; sta[++sta[0]]=x;
    st.push(x);
    while (!st.empty()){
        int u=st.top();
        int i=fre[u];
        if (now[u]){
            i=now[u];
            low[u]=min(low[u],low[go[i]]);
            i=next[i];
        }
        while (i){
            if (!dfn[go[i]]) {
                now[u]=i;
                dfn[go[i]]=low[go[i]]=++k;
                sbz[go[i]]=true;
                st.push(go[i]);
                sta[++sta[0]]=go[i];
                break;
            }
            else if (sbz[go[i]]) low[u]=min(low[u],dfn[go[i]]);
            i=next[i];
        }
        if (u==st.top()){
            ++tt; int j=0;
            if(dfn[u]==low[u]){
                while (sta[sta[0]]!=u){
                    int k=sta[sta[0]];
                    ++j;
                    t[k]=tt;
                    pro[tt]+=pro[k];
                    sbz[k]=false;
                    --sta[0];
                }
                sbz[u]=false; 
                --sta[0];
                ++j;
                if (j==1) --tt;
                else {
                    t[u]=tt;
                    pro[tt]+=pro[u];
                }
            }
            st.pop();
        }
    }
}
void spfa(int x)
{
    dis[x]=pro[x]; bz[x]=true; hg[1]=x;
    int h=0,t=1;
    while (h!=t){
        h=h%mo+1;
        int u=hg[h];
        bz[u]=false;
        int i=fre[u];
        while (i){
            if (dis[go[i]]if (!bz[go[i]]){
                    bz[go[i]]=true;
                    t=t%mo+1;
                    hg[t]=go[i];
                }
            }
            i=next[i];
        }
    }
}
int main()
{
    //freopen("T.in","r",stdin);
    scanf("%d%d",&n,&m);
    fo(i,1,m){
        scanf("%d%d",&a[i][1],&a[i][2]);
        add(a[i][1],a[i][2]);
    }
    fo(i,1,n) scanf("%d",&pro[i]);
    scanf("%d%d",&S,&p);
    fo(i,1,p) scanf("%d",&keep[i]);
    tt=n;
    tarjan(S);
    fo(i,1,max(n,num)){
        fre[i]=0; go[i]=0; next[i]=0; 
    }
    num=0;
    fo(i,1,m){
        if (!t[a[i][1]] && !t[a[i][2]]) add(a[i][1],a[i][2]);
        else if (t[a[i][1]] && !t[a[i][2]] ) add(t[a[i][1]],a[i][2]);
        else if (!t[a[i][1]] && t[a[i][2]]) add(a[i][1],t[a[i][2]]);
        else if (t[a[i][1]]&&t[a[i][2]]&&t[a[i][1]]!=t[a[i][2]]) 
            add(t[a[i][1]],t[a[i][2]]);
    }
    if (!t[S]) spfa(S); else spfa(t[S]);
    fo(i,1,p) 
    if (t[keep[i]]){
        if (anselse if (ansprintf("%lld",ans);
} 

爬山【NOIP2016提高A组模拟9.10】_第1张图片

你可能感兴趣的:(信息技术,图论,强连通分量)