题目大意:
N个小朋友有M组单向关系,现在询问一个小朋友能得到的最多支持有多少,输出这个最大值和这些小朋友的编号。
问题分析:
其实就是,一个点的贡献等于能到达它的点的个数。求这个最大贡献并且输出所有满足条件的点编号。考虑到有向图中的一个环相互传递,先dcc缩点后形成若干个点,由于前面的边的单向关系,导致我们要算贡献的那个缩点的出度必定是0。因此直接对缩点后的图反向建图,入度为0的点就是可能的答案,以它为起点计算它能经过图中的点权和,注意搜索时打上标记 。
#include<bits/stdc++.h>
#include<cstring>
#include<cstdio>
#include<queue>
#define Re register int
using namespace std;
const int N=5e4+300,M=1e5+3;
int n,m,x,y,Q_o,ans,A[N],ru[N],gs[N],ip[N],dp[N],cu[N],flag[N];
inline void in(Re &x)
{
int f=0;
x=0;
char c=getchar();
while(c<'0'||c>'9')
f|=c=='-',c=getchar();
while(c>='0'&&c<='9')
x=(x<<1)+(x<<3)+(c^48),c=getchar();
x=f?-x:x;
}
vector<int>dcc[N];
inline int min(Re a,Re b)
{
return a<b?a:b;
}
inline int max(Re a,Re b)
{
return a>b?a:b;
}
struct Tarjan
{
int o,t,dfn_o,Q[N],low[N],dfn[N],pan[N],head[N];
struct QAQ
{
int x,to,next;
} a[M];
inline void add(Re x,Re y)
{
a[++o].x=x,a[o].to=y,a[o].next=head[x],head[x]=o;
}
inline void cle()
{
memset(head,0,sizeof(head));
memset(low,0,sizeof(low));
memset(dfn,0,sizeof(dfn));
memset(gs,0,sizeof(gs));
memset(ip,0,sizeof(ip));
memset(pan,0,sizeof(pan));
dfn_o=t=o=0;
Q_o=0;
}
inline void tarjan(Re x)
{
dfn[x]=low[x]=++dfn_o,Q[++t]=x,pan[x]=1;
for(Re i=head[x],to; i; i=a[i].next)
if(!dfn[to=a[i].to])
tarjan(to),low[x]=min(low[x],low[to]);
else if(pan[to])
low[x]=min(low[x],dfn[to]);
if(low[x]==dfn[x])
{
++Q_o;
while(1)
{
dcc[Q_o].push_back(Q[t]);
ip[Q[t]]=Q_o,gs[Q_o]+=A[Q[t]],pan[Q[t]]=0;
if(x==Q[t--])
break;
}
}
}
inline void SuoPoint()
{
for(Re i=1; i<=n; ++i)
if(!dfn[i])
tarjan(i);
}
} T1;
struct Tuopu
{
int o,pan[N],head[N];
std::queue<int>Q;
struct QAQ
{
int to,next;
} a[M<<1];
inline void add(Re x,Re y)
{
a[++o].to=y,a[o].next=head[x],head[x]=o;
}
inline void cle()
{
memset(head,0,sizeof(head));
memset(dp,0,sizeof(dp));
o=0;
memset(ru,0,sizeof(ru));
memset(cu,0,sizeof(cu));
}
inline int dfs(Re rt)
{
int add=0;
flag[rt]=1;
for(int i=head[rt]; i; i=a[i].next)
{
if(!flag[a[i].to])
{
flag[a[i].to]=1;
add=add+gs[a[i].to]+dfs(a[i].to);
}
}
return add;
}
inline void creat()
{
for(Re i=1; i<=T1.o; ++i)
if((x=ip[T1.a[i].x])!=(y=ip[T1.a[i].to]))
{
++ru[x],add(y,x),++cu[y];
}
}
inline void tuopu()
{
for(Re i=1; i<=Q_o; ++i)
if(!ru[i])
dp[i]=gs[i],Q.push(i);
while(!Q.empty())
{
Re x=Q.front();
Q.pop();
for(Re i=head[x],to; i; i=a[i].next)
{
to=a[i].to;
dp[to]=max(dp[to],dp[x]+gs[to]);
if(!(--ru[to]))
Q.push(to);
}
}
}
} T2;
vector<int>anss;
vector<int>ap;
int main()
{
Re cases=0;
in(cases);
Re ca=0;
while(cases--)
{
in(n),in(m);
ca++;
for(Re i=1; i<=n; ++i)
A[i]=1;
T1.cle();
T2.cle();
while(m--)
in(x),in(y),T1.add(x+1,y+1);
T1.SuoPoint();
T2.creat();
int sum=0;
for(int i=1; i<=Q_o; i++)
{
if(!ru[i])
{
memset(flag,0,sizeof(flag));
int fuck= T2.dfs(i)+gs[i]-1;
if(fuck>sum)
{
sum=fuck;
anss.clear();
anss.push_back(i);
continue;
}
if(fuck==sum)
{
anss.push_back(i);
}
}
}
printf("Case %d: %d\n",ca,sum);
for(int i=0; i<anss.size(); i++)
{
for(int j=0; j<dcc[anss[i]].size(); j++)
{
ap.push_back(dcc[anss[i]][j]-1);
}
}
sort(ap.begin(),ap.end());
for(int i=0; i<ap.size(); i++)
{
printf("%d",ap[i]);
if(i!=ap.size()-1)
printf(" ");
}
ap.clear();
anss.clear();
for(int i=1; i<=Q_o; i++)
{
dcc[i].clear();
}
printf("\n");
}
}
提供一组特例
6 6 N M
0 1
0 2
1 5
1 3
2 3
2 4
反向建图后
答案应该为 3 3