题意
给定一棵有向树,求有向树的必经点。
题解1
一种对于所有有向图都是用的方法,求出从S到x的方案数f[x],和从T到x的方案数g[x]。对于一个点如果有f[x]*g[x]=f[T],那么x是必经点,
对于必经边的求法也提一下,同上求出f[x]和g[x],一条边(x,y)如果有f[x]*g[x]=f[T],则这条边是必经边。
实际上f和g是相当大的数字,我们可以采用取模的方案来存,如果你喜欢高精度我也不会拦你,多取几个模数更稳,详见代码。
代码
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
typedef long long ll;
const ll P=10007,Q=1000000007;
const int MAXN=100010;
inline void write(int x)
{
if(x>9) write(x/10);
putchar(x%10|48);
}
int n,S,T;
int head,tail,q[MAXN];
int ru[MAXN],chu[MAXN];bool beg[MAXN];
struct E{int y,next;}e[MAXN*2],Fe[MAXN*2];int len=0,last[MAXN],Flast[MAXN];
void ins(int x,int y)
{
ru[y]++;chu[x]++;
e[++len]=(E){y,last[x]};last[x]=len;
Fe[len]=(E){x,Flast[y]};Flast[y]=len;
}
ll fp[MAXN],fq[MAXN],gp[MAXN],gq[MAXN];
void bfs1()
{
head=0,tail=1;q[0]=S;
fp[S]=fq[S]=1;
while(head<tail)
{
int x=q[head++];
for(int k=last[x];k;k=e[k].next)
{
int y=e[k].y;
fp[y]=(fp[y]+fp[x])%P;
fq[y]=(fq[y]+fq[x])%Q;
ru[y]--;
if(!ru[y]) q[tail++]=y;
}
}
}
void bfs2()
{
head=0,tail=1;q[0]=T;
gp[T]=gq[T]=1;
while(head<tail)
{
int x=q[head++];
for(int k=Flast[x];k;k=Fe[k].next)
{
int y=Fe[k].y;
gp[y]=(gp[y]+gp[x])%P;
gq[y]=(gq[y]+gq[x])%Q;
chu[y]--;
if(!chu[y]) q[tail++]=y;
}
}
}
int main()
{
scanf("%d",&n);S=0,T=n+1;
for(int i=1;i<n;i++)
{
int x,y;
scanf("%d%d",&x,&y);
ins(x,y);
}
for(int i=1;i<=n;i++)
if(!ru[i]) ins(S,i),beg[i]=true;
else if(!chu[i]) ins(i,T);
bfs1();
bfs2();
for(int i=1;i<=n;i++) if(!beg[i])
if(fp[i]*gp[i]%P==fp[T] && fq[i]*gq[i]%Q==fq[T]) write(i),puts("");
return 0;
}
题解2
题目的隐含条件它给出来的点标是拓扑序,而且又是一棵树,那么我们有更加special的做法来应对。
可以发现,这些点应该是连续的一段,所以可以从出度入度来考虑这个问题。用两个指针标记一下。下面这个代码不是我的...
小结
如果想要大幅提速一定要把握好题目特性。
代码
#include<iostream>
#include<cstdio>
using namespace std;
const int maxn=100001;
int n,i,x,y,s,t,rd[maxn],cd[maxn],next[maxn];
inline int read()
{
int x=0;
char ch=getchar();
while (ch<'0'||ch>'9') ch=getchar();
while (ch>='0'&&ch<='9') x=x*10+ch-'0',ch=getchar();
return x;
}
int main()
{
n=read();
for(i=1;i<n;i++)
{
x=read();
y=read();
next[x]=y;
++cd[x];
++rd[y];
}
s=t=-1;
for(i=1;i<=n;i++)
{
if (t==i&&(s<0||rd[i]>1)) s=i;
if (cd[i]!=1) break;
if (next[i]>t) t=next[i];
}
for(i=s;i<=n;i=next[i])
{
printf("%d\n",i);
if (cd[i]!=1) break;
}
return 0;
}