P6134 [JSOI2015]最小表示
洛谷传送门
Solution
显然可以看出来的一点是:一条边
那我们可以贪心的去考虑,对于每一对点对
具体实现可以对原图进行一遍拓扑排序,在过程中记录入队的时间。
对每个点,因为最长路径上一定包含先访问的点,所以把和它直接连接的点按照时间戳排序,维护连通性,统计答案即可。
但是,如果正序去考虑,我们是不知道点之间联通性的,所以倒序处理。
此时时间复杂度是
现在复杂度是
小细节:因为无环,所以删一条边和别的边没有关联,也就是相互独立
Code
#include<cmath>
#include<queue>
#include<bitset>
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
const int N=30010;
struct edge{
int to,nxt;
}e[N*10];
int head[N],cnt,n,m;
int vis[N],in[N],ind[N];
bitset<N>rd[N];//联通性
int topu[N];
inline int read(){
int x=0,f=1;
char ch=getchar();
while(!isdigit(ch)){if(ch=='-') f=-1;ch=getchar();}
while(isdigit(ch)){x=x*10+(ch^48);ch=getchar();}
return x*f;
}
inline void add(int u,int v){
e[++cnt].to=v;
e[cnt].nxt=head[u];
head[u]=cnt;
}
inline bool cmp(const int &a,const int &b){
return vis[a]<vis[b];
}
inline void topsort(){
queue<int> q;
for(int i=1;i<=n;i++)
if(ind[i]==0) q.push(i);
int tim=0;
while(!q.empty()){
int u=q.front();q.pop();
in[++tim]=u;
vis[u]=tim;
for(int i=head[u];i;i=e[i].nxt){
int v=e[i].to;
ind[v]--;
if(ind[v]==0) q.push(v);
}
}
int ans=0;
for(int i=n;i>=1;i--){
int x=in[i],tot=0;
rd[x][x]=1;
for(int j=head[x];j;j=e[j].nxt)
topu[++tot]=e[j].to;
sort(topu+1,topu+tot+1,cmp);
for(int j=1;j<=tot;j++){
if(rd[x][topu[j]]) ans++;
else rd[x]|=rd[topu[j]];
}
}
printf("%d\n",ans);
}
int main(){
n=read();m=read();
for(int i=1;i<=m;i++){
int u=read(),v=read();
add(u,v);
ind[v]++;
}
topsort();
return 0;
}