[拓扑排序][DP][Tarjan][并查集]JZOJ 4253 QYQ在艾泽拉斯

Description

在艾泽拉斯的无尽之海里,有着一群不为人知的由各个种族的冒险者统治的岛屿,这些岛屿都很庞大,足以在上面建造许多的城市,城市之间有一些单向道路连接。
有一天,QYQ无意中发现了这些岛屿,并且发现在每个城市的地下都或多或少埋藏着一些装备、金币、宝物……
可是正当QYQ兴奋不已打算全部把它们拿走时,他却惊奇的发现你的魔法在这里被限制住了,唯一可用的技能就是闪现,而且魔法只够他使用K次这个技能了,每次使用这个技能QYQ只能从一个岛屿上闪现到另外一个岛屿上。每一个岛屿只能登上一次,QYQ可以从任何一个城市开始旅程,在任何一个城市结束旅程。
城市的数量共有n个,有m条道路,每一条道路有两个参数u,v,表示从u到v有一条道路,但你只能由u到v走,两个城市属于相同的岛屿当且仅当暂时将所有道路视为双向道路时可以从其中一个城市走到另一个城市(可以途径其它城市)。
每一个城市都有一个宝物的总价值v[i],你的任务是帮助QYQ得到最大总价值的宝物,并输出这个值。

Input

从文件azeroth.in中输入数据。
输入的第一行包含两个整数n,m
输入的第二行到第m+1行,每行包含2个整数u,v,代表你可以从城市u走到城市v
输入的第m+2行包含n个整数,第i个整数代表v[i],即这个城市的宝物总价值。
输入的第m+3行包含一个整数K,代表你可以使用技能的次数。

Output

输出到文件azeroth.out中。
输出的第一行包含一个整数,代表QYQ能获得的最大的宝物总价值

Sample Input

3 21 23 11 2 10

Sample Output

4样例说明:QYQ从3号点开始,走到2号点,最后走到1号点,结束旅程,共获得1+2+1=4价值的宝物

Data Constraint

对于30%的数据:n<=10,K=0
对于50%的数据:n<=100,m<=100,K<=1
对于100%的数据:1<=n<=100000,1<=m<=1000000,1<=v[i]<=1000,0<=K<=100000
图中可能会有重边、自环。

分析

水题

tarjan缩完点在拓扑序上随便DP,并查集维护连通块

#include <iostream>
#include <cstdio>
#include <memory.h>
#include <queue>
#include <algorithm>
using namespace std;
const int N=1e5+10;
struct Edge {
    int u,v,nx;
}g[10*N];
int cnt,list[N],val[N],w[N],f[N],deg[N],fa[N],ans[N];
int low[N],dfn[N],tme;
int stk[N],top,bel[N],id,stack[N],tp,icnt;
bool instk[N],vis[N];
int n,m,k;

int Get_F(int x) {return fa[x]==x?x:fa[x]=Get_F(fa[x]);}

void Add(int u,int v) {g[++cnt]=(Edge){u,v,list[u]};list[u]=cnt;}

void Tarjan(int v0) {
    stack[++tp]=v0;
    while (tp) {
        int u=stack[tp],i;
        if (!dfn[u]) low[u]=dfn[u]=++tme,stk[++top]=u,instk[u]=1;
        for (i=list[u];i;i=g[i].nx)
            if (!dfn[g[i].v]) break;
            else if (instk[g[i].v]) low[u]=min(low[u],dfn[g[i].v]);
        if (!i&&tp>1) low[stack[tp-1]]=min(low[stack[tp-1]],low[u]);
        if (!i) {
            if (dfn[u]==low[u]) {
                ++id;
                do {
                    bel[stk[top]]=id;instk[stk[top]]=0;w[id]+=val[stk[top]];
                }
                while (stk[top--]!=u);
            }
            tp--;
        }
        else stack[++tp]=g[i].v;
    }
}

bool CMP(int a,int b) {
    return a>b;
}

void ToppoSort() {
    queue<int> q;
    while (!q.empty()) q.pop();
    for (int i=1;i<=id;i++) if (!deg[i]) q.push(i);
    while (!q.empty()) {
        int u=q.front();q.pop();f[u]+=w[u];
        for (int i=list[u];i;i=g[i].nx) {
            f[g[i].v]=max(f[g[i].v],f[u]);deg[g[i].v]--;
            if (!deg[g[i].v]) q.push(g[i].v);
        }
    }
    for (int i=1;i<=id;i++)
        ans[Get_F(i)]=max(ans[Get_F(i)],f[i]),icnt+=!vis[Get_F(i)],vis[Get_F(i)]=1;
    sort(ans+1,ans+n+1,CMP);
    int lans=0;
    for (int i=1;i<=min(k+1,icnt);i++) lans+=ans[i];
    printf("%d\n",lans);
}

int main() {
    freopen("azeroth.in","r",stdin);freopen("azeroth.out","w",stdout);
    scanf("%d%d",&n,&m);
    for (int i=1,u,v;i<=m;i++) scanf("%d%d",&u,&v),Add(u,v);
    for (int i=1;i<=n;i++) scanf("%d",&val[i]);
    scanf("%d",&k);
    for (int i=1;i<=n;i++) if (!dfn[i]) Tarjan(i);
    cnt=0;memset(list,0,sizeof list);
    for (int i=1;i<=id;i++) fa[i]=i;
    for (int i=1;i<=m;i++)
        if (bel[g[i].u]!=bel[g[i].v])
            deg[bel[g[i].v]]++,fa[Get_F(bel[g[i].u])]=Get_F(bel[g[i].v]),Add(bel[g[i].u],bel[g[i].v]);
    ToppoSort();
}

原文地址:https://www.cnblogs.com/mastervan/p/10339736.html

时间: 2024-08-27 02:29:46

[拓扑排序][DP][Tarjan][并查集]JZOJ 4253 QYQ在艾泽拉斯的相关文章

HDU1811 拓扑排序判环+并查集

HDU Rank of Tetris 题目:http://acm.hdu.edu.cn/showproblem.php?pid=1811 题意:中文问题就不解释题意了. 这道题其实就是一个拓扑排序判圈,我的博客里面其他几篇拓扑排序判圈的套路一样.但是这道题与他们不同的的是在大小关系里面存在一种 "="的关系,这就意味的那些序号不同的点,实际上是一个点.共享入度和出度.我们可以通过并查集将他们合并,合成一个点.这里说一下如何判断信息不完全.我们早先在做拓扑排序,多种排列方式的时候,按照字

POJ 3249 Test for Job 拓扑排序+DP

http://poj.org/problem?id=3249 题意: 给一个有向无环图DAG(不一定联通),每个点有权值,入度为0的点为起点,出度为0的点为终点,选择一个起点走到一个终点,使得路上的权和最大. 分析: dp[to] = max(dp[from]) + value[to],然后先拓扑排序保证状态正确转移即可,终点做标记,如果是终点则尝试更新答案. update:因为点权可以为负,所以程序里用dp[i] == -1表示未访问过该点是有问题的,不过没有遇上会卡掉这种情况的数据=.= 1

POJ 3249 拓扑排序+DP

貌似是道水题.TLE了几次.把所有的输入输出改成scanf 和 printf ,有吧队列改成了数组模拟.然后就AC 了.2333333.... Description: MR.DOG 在找工作的过程中呢.遇见了这样一个问题.有n个城市,m条小道.然后要从入度为0的点出发,出度为0的点结束,中途经过的城市呢,都是要付费的.负数表示花费.正数表示收益.然后让你求收益最大或者说花费最少的总值. 貌似.BFS和DFS都会超时.不妨一试.附代码: #include<stdio.h>#include<

[Luogu P3953] 逛公园 (最短路+拓扑排序+DP)

题面 传送门:https://www.luogu.org/problemnew/show/P3953 Solution 这是一道神题 首先,我们不妨想一下K=0,即求最短路方案数的部分分. 我们很容易可以想到一个做法,就是魔改迪杰斯特拉做法: 如果一个点可以更新到达其他点的距离,那个点的方案数就是这个点的方案数:如果一个点所更新出来的距离和之前的相等,那个点的方案数加等当前点的方案数. 用式子可以表现为: f[j]=f[i] (dis[j]>dis[i]+x)   f[j]+=f[i] (dis

【tarjan 拓扑排序 dp】bzoj1093: [ZJOI2007]最大半连通子图

思维难度不大,关键考代码实现能力.一些细节还是很妙的. Description 一个有向图G=(V,E)称为半连通的(Semi-Connected),如果满足:?u,v∈V,满足u→v或v→u,即对于图中任意两点u,v,存在一条u到v的有向路径或者从v到u的有向路径.若G'=(V',E')满足V'?V,E'是E中所有跟V'有关的边,则称G'是G的一个导出子图.若G'是G的导出子图,且G'半连通,则称G'为G的半连通子图.若G'是G所有半连通子图中包含节点数最多的,则称G'是G的最大半连通子图.给

[ZJOI2007]最大半连通子图 (Tarjan缩点,拓扑排序,DP)

题目链接 Solution 大概是个裸题. 可以考虑到,如果原图是一个有向无环图,那么其最大半联通子图就是最长的一条路. 于是直接 \(Tarjan\) 缩完点之后跑拓扑序 DP就好了. 同时由于是拓扑序DP,要去掉所有的重边. Code #include<bits/stdc++.h> #define ll long long using namespace std; const int maxn=100008; struct sj{int to,next;}a[maxn*10]; ll mo

hdu-2874 Connections between cities(lca+tarjan+并查集)

题目链接: Connections between cities Time Limit: 10000/5000 MS (Java/Others)     Memory Limit: 32768/32768 K (Java/Others) Problem Description After World War X, a lot of cities have been seriously damaged, and we need to rebuild those cities. However, s

POJ3249Test for Job(拓扑排序+DP)

题意就是给一个有向无环图,每个点都有一个权值,求从入度为0的点到出度为0点路径上经过点(包括起点终点)的权值和的最大值. 分析: 注意3点 1.本题有多组数据 2.可能有点的权值是负数,也就是结果可能为负,初值要设为负无穷. 3.入度或出度为0的点不止一个. 注意以上几点本题就很简单了,用到DP dis[i]:=max(dis[j],dis[i]+w[j])在拓扑排序过程同时进行即可. 考前练练拓扑排序和指针. 代码: program test; type point=^node; node=r

POJ3249 Test for Job(拓扑排序+dp)

Test for Job Time Limit: 5000MS   Memory Limit: 65536K Total Submissions: 10137   Accepted: 2348 Description Mr.Dog was fired by his company. In order to support his family, he must find a new job as soon as possible. Nowadays, It's hard to have a jo