题目描述:
在一个有向图中,Ralph从某一个起点出发,去采蘑菇,每条道路的权重代表该道路上蘑菇的初始值,每次经过这条道路时,Ralph会将这条道路上的所有蘑菇都采掉,然后蘑菇会又长出来,但是会比上一次的蘑菇少,第一次走过后蘑菇会少一个,第二次走过后蘑菇会少两个,直到蘑菇少完。比如某条路一开始有5个蘑菇,第一次走过后,还有5-1=4个蘑菇,第二次走过后,还有4-2= 2个蘑菇,第三次走过后,不剩蘑菇。现在给出这个有向图和图中的边的权值,求Ralph最多能拿到多少蘑菇。
算法思想:
想一下,如果要拿到最多的蘑菇,能到达的环一定要把每个边上的所有蘑菇都拿得不能再拿了,简单来看就是先缩点,然后DAG上的最长路就可以了。
具体实现:
首先先跑一遍Tarjan,把SCC跑出来,再把SCC构建成一个新的DAG,在这个DAG上图DP跑最长路就可以得到最后结果了。
贴一下代码
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <queue>
using namespace std;
typedef long long ll;
const int maxn = 1000010;
int head[maxn],head1[maxn],DFN[maxn],LOW[maxn];
int belong[maxn],Stack[maxn];
bool vis[maxn];
bool instack[maxn];
int du[maxn],L[maxn];
ll d[maxn];
int n,m;
ll V[maxn];
int Cnt,Bcnt,Index,Top,Cnt1;
void init()
{
Cnt = 1;
Cnt1 = 1;
Top = 0;
Index = 0;
Bcnt = 0;
memset(vis,false,sizeof(vis));
memset(d,0,sizeof(d));
memset(head,-1,sizeof(head));
memset(head1,-1,sizeof(head1));
memset(V,0,sizeof(V));
memset(DFN,0,sizeof(DFN));
memset(belong,0,sizeof(belong));
memset(instack,false,sizeof(instack));
}
struct Edge{
int to;
int next;
ll val;
int from;
}edge[maxn],edge1[maxn];
// edge is the graph before ,edge1 is the DAG of SCC
void addEdge(int a,int b,ll v)
{
edge[Cnt].to = b;
edge[Cnt].next = head[a];
edge[Cnt].val = v;
edge[Cnt].from = a;
head[a] = Cnt++;
}
void Tarjan(int i)
{
int j;
DFN[i] = LOW[i] = ++Index;
instack[i] = true;
Stack[++Top] = i;
for(j = head[i];j != -1;j = edge[j].next)
{
int v = edge[j].to;
if(!DFN[v])
{
Tarjan(v);
if(LOW[v] < LOW[i])
LOW[i] = LOW[v];
}
else if(instack[v] && DFN[v] < LOW[i])
LOW[i] = DFN[v];
}
if(DFN[i] == LOW[i])
{
Bcnt++;
do{
j = Stack[Top--];
instack[j] = false;
belong[j] = Bcnt;
}while(j!=i);
}
}
ll Value(ll j) // for every value j we can calc how
//many mushrooms can we get from that edge
{
ll left = 1;
ll Right = j;
ll midd;
while(left < Right - 1)
{
midd = (left + Right) / 2;
if((midd*(midd+1))/2 < j)
{
left = midd;
}
else if((midd*(midd+1))/2 > j)
{
Right = midd;
}
else
{
break;
}
}
midd = (left+Right)/2;
ll ans = 0;
ans = ans + j*(midd+1);
ans = ans - (midd)*(midd+1)*(midd+2)/6;
return ans;
}
void addEdge1(int a,int b,ll v)
{
edge1[Cnt1].to = b;
edge1[Cnt1].next = head1[a];
edge1[Cnt1].val = v;
edge1[Cnt1].from = a;
head1[a] = Cnt1++;
}
void Build_Value() //Calc every value of SCC
{
int i;
for(i = 1;i <= Cnt;i ++)
{
if(belong[edge[i].from] == belong[edge[i].to])
{
V[belong[edge[i].from]] += Value(edge[i].val);
}
else
{
addEdge1(belong[edge[i].from],belong[edge[i].to],edge[i].val);
}
}
}
void toposort() // Run topological sort
{
memset(du,0,sizeof(du));
for(int i = 1;i <= Bcnt;++ i)
{
for(int j = head1[i];j != -1;j = edge1[j].next)
{
int v = edge1[j].to;
du[v] ++;
//printf("%d %d\n",edge1[j].from,edge1[j].to);
}
}
int tot = 0;
queue<int>Q;
for(int i = 1;i <= Bcnt;++ i)
{
if(!du[i])Q.push(i);
}
while(!Q.empty())
{
int x = Q.front();
Q.pop();
L[++tot] = x;
for(int j = head1[x];j != -1;j = edge1[j].next)
{
int v = edge1[j].to;
du[v] --;
if(!du[v])Q.push(v);
}
}
}
void dp(int j) // Run Dp on DAG to find the Longest road
{
d[j] += V[j];
int s;
for(s = head1[j];s != -1;s = edge1[s].next)
{
int v = edge1[s].to;
vis[v] = true;
d[v] = max(d[v],d[j] + edge1[s].val);
// printf("%d\n",edge1[s].val);
}
}
void debug() //just for debug
{
for(int i = 1;i <= n;++ i)
{
//printf("%d\n",belong[i]);
}
for(int i = 1;i <= Bcnt;++ i)
{
printf("%d\n",L[i]);
}
}
int main()
{
init();
scanf("%d%d",&n,&m);
for(int i = 1;i <= m;++ i)
{
int x,y;
ll v;
scanf("%d%d%lld",&x,&y,&v);
addEdge(x,y,v);
}
int st;
scanf("%d",&st);
for(int i = 1;i <= n;++ i)
if(!DFN[i])
Tarjan(i);
Build_Value();
toposort();
vis[belong[st]] = true;
for(int i = 1;i <= Bcnt;++ i)
{
if(vis[L[i]])
dp(L[i]);
}
// debug();
ll MAX = 0;
for(int i = 1;i <= Bcnt;++ i)
{
MAX = max(MAX,d[i]);
}
printf("%lld\n",MAX);
return 0;
}
感觉整个题目只要思路清晰,解决起来是没有问题的。毕竟我这个大水X都写出来了