【51nod】调查任务(tarjan+dp)

基准时间限制:4 秒 空间限制:524288 KB 分值: 80
lbn是战忽中心——一个绝密的军事组织的一个军官,今天他接到了一个紧急任务:调查敌国X国某些城市的经济情况。
X国有N个城市,由M条单向道路连接,其中S城是X国的首都。
每个城市i有一个发达指数a[i],我们定义城市i的经济状况为首都S到城市i任意一条路径上两个不同的城市x,y的a[x] mod a[y]的最大值。(x和y必须在同一条路径上,x,y可以是i或者S)

lbn当然能轻松地完成这个任务,但他想考考你。

样例解释:
首都为2
2到1只有一条路径,城市1的经济情况为a[2] mod a[1]=23
对于城市3的经济状况,我们可以选择路径2->4->3,并选择城市3 4,经济状况为a[3] mod a[4]=37,可以发现这是最大值。
对于城市4的经济状况,我们可以选择路径2->3->4,并选择城市3 4,经济状况为a[3] mod a[4]=37,可以发现这是最大值。

一个点可以被经过多次!

数据已加强

Input
第一行四个正整数N,M,Q,S
分别表示X国城市数量,城市间有向边的数量,需要调查的城市的数目和首都的编号。每个城市的标号为1到N
第二行N个正整数,其中第i个整数表示a[i]。
第2至M+1行每行两个正整数x,y。表示有一条从城市x到y有向边。
第M+2行Q个正整数,表示需要调查的城市的数目的编号。
数据保证无重边无自环,不会查询首都。

1<=N,Q<=4*10^5
1<=M<=2*10^6
1<=a[i]<=10^9
Output
共一行Q个整数,按询问顺序输出每个城市的经济情况。
如果一个城市不存在任何一条从首都来的路径,则其经济情况输出-1。
Input示例
4 5 3 2
98 23 37 100
2 1
2 4
2 3
3 4
4 3
1 3 4
Output示例
23 37 37

这题细节各种多。。。关键如何在dag上从s到某个点上的次小值,我们设4个量就可以类似dp递推了。
设:
dp[0][v] d p [ 0 ] [ v ] :为从s到v路径上的最大值
dp[1][v] d p [ 1 ] [ v ] :为从s到v路径上的次大值(答案)
dp[2][v] d p [ 2 ] [ v ] :为从s到v路径上经过的所有可能的点的最大值
dp[3][v] d p [ 3 ] [ v ] :为从s到v路径上经过的所有可能的点的次大值

然后通过分类讨论就可以证明他们是可以递推的,即我们选中的答案我们可以保证在s到这个点的某条路径会有某个值严格大于它。

代码:

#include<iostream>
#include<cstdio>
#include<stack>
#include<algorithm>
#include<queue>
#include<cstring>
#include<cmath>
#include<vector>
#include<stack>
#define maxn 400005
#define ll long long
#define INF 0x3f3f3f3f
using namespace std;
vector<int>g[maxn],G[maxn];
int DFN[maxn],low[maxn],index;
int st[maxn],cnt;
bool inS[maxn];
int dp[4][maxn];
inline int _min(int a,int b)
{
    return a>b?b:a;
}
inline int _max(int a,int b)
{
    return a>b?a:b;
}
int be[maxn];
int tot;
int a[maxn];
void tarjan(int v)
{
    DFN[v]=low[v]=++index;
    st[++cnt]=v;inS[v]=true;
    for(int i=0,num=g[v].size();i<num;++i)
    {
        int u=g[v][i];
        if(!DFN[u])
        {
            tarjan(u);
            low[v]=_min(low[v],low[u]);
        }
        else
            if(inS[u])low[v]=_min(low[v],DFN[u]);
    }
    if(DFN[v]==low[v])
    {
        ++tot;int now;
        do
        {
            now=st[cnt]; --cnt;
            be[now]=tot;
            inS[now]=false;

            if(a[now]>dp[0][tot])dp[1][tot]=dp[0][tot],dp[0][tot]=a[now];
            else if(a[now]<dp[0][tot]&&a[now]>dp[1][tot])dp[1][tot]=a[now];
            dp[2][tot]=dp[0][tot],dp[3][tot]=dp[1][tot];
        }while(now!=v);
    }
}
queue<int> que;
void update(int u,int v)//核心是这个函数,好好想想,可以分类讨论证明
{
   dp[1][v]=_max(dp[1][v],dp[1][u]);
    if(dp[0][v]!=dp[2][u])dp[1][v]=_max(dp[1][v],_min(dp[0][v],dp[2][u]));
    else dp[1][v]=_max(dp[1][v],dp[3][u]);

    if(dp[2][u]>dp[2][v])dp[3][v]=dp[2][v],dp[2][v]=dp[2][u];
    else if(dp[2][v]>dp[2][u]&&dp[2][u]>dp[3][v]) dp[3][v]=dp[2][u];

    if(dp[3][u]>dp[2][v])dp[3][v]=dp[2][v],dp[2][v]=dp[3][u];
    else if(dp[2][v]>dp[3][u]&&dp[3][u]>dp[3][v])dp[3][v]=dp[3][u];
}
int deg[maxn];
bool vis[maxn];
void bfs(int r)
{
    queue<int> que;
    que.push(r);
    vis[r]=true;
    while(que.size())
    {
        int u=que.front();que.pop();
        for(int i=0,num=G[u].size();i<num;++i)
        {
            int v=G[u][i];
            ++deg[v];
            if(!vis[v])
            {
                vis[v]=true;
                que.push(v);
            }
        }
    }
}
void topo(int r)
{
    que.push(r);
    while(que.size())
    {
        int u=que.front();que.pop();
        for(int i=0,num=G[u].size();i<num;++i)
        {
            int v=G[u][i];
            update(u,v);
            --deg[v];if(!deg[v])que.push(v);
        }
    }
}
void init(){}
int main()
{
    int n,m,q,s;
    cin>>n>>m>>q>>s;
    init();
    for(int i=1;i<=n;++i)
        scanf("%d",a+i);
    int x,y;
    for(int i=0;i<m;i++)
    {
        scanf("%d%d",&x,&y);
        g[x].push_back(y);
    }
    for(int i=1;i<=n;++i)
        if(!DFN[i]) tarjan(i);
//    for(int i=1;i<=n;i++)
//        cout<<i<<" "<<be[i]<<endl;
    for(int u=1;u<=n;++u)
        for(int i=0,num=g[u].size();i<num;++i)
        {
            int v=g[u][i];
            if(be[u]!=be[v])
                G[be[u]].push_back(be[v]);
        }
    bfs(be[s]);
    topo(be[s]);
    while(q--)
    {
        scanf("%d",&x);
        if(vis[be[x]])printf("%d ",dp[1][be[x]]);
        else printf("-1 ");
    }
    return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值