\(\text{Solution}\)

首先发现这题是可以二分的。

我们二分枚举所选边边权的最大值,接下来就是判断随意改变边权小于等于最大值的边的方向能否形成有向无环图。

我先讲一下判断的方法再证明:无视边权小于等于最大值的边进行拓扑排序,如果此图不连通或有环则不行,反之可以。在拓扑排序中记录每个点被访问的时间戳,对于边权小于等于最大值的有向边 \((u,v)\),如果 \(Time[v]<Time[u]\),就将这条边反向。

虽然忽略边权小于等于最大值的边会造成图的某部分断裂,但如果全断,反而那个点会变成拓扑排序的起始点,所以不会影响判断图是否连通,有环判断显然。

接下来证明反向的方法是对的。

  • 如果环中有几条边权小于等于最大值的边。如果可能有 \(n\) 条,即使有 \(n-1\) 条连续的边满足 \(Time[u]<Time[v]\)\(n-1\) 条就是 \(n\) 个起始点连接,少一点起始点则两个不同起始点的段有 \(Time[u]>Time[v]\)),最后一条边一定是 \(Time[u]>Time[v]\),就不可能有环。

  • 如果与外界连接的点除了与外界连接有一条边权大于最大值的边指向它,因为是拓扑,外界的那条边相当于没有用,顺序可以保证。如果与外界连接的点除了与外界连接有一条边权小于等于最大值的边指向它,就同上一条了。

\(\text{emm}\) 感觉很乱。

\(\text{Code}\)

#include <cstdio>

#define rep(i,_l,_r) for(register signed i=(_l),_end=(_r);i<=_end;++i)
#define fep(i,_l,_r) for(register signed i=(_l),_end=(_r);i>=_end;--i)
#define erep(i,u) for(signed i=head[u],v=to[i];i;i=nxt[i],v=to[i])
#define efep(i,u) for(signed i=Head[u],v=to[i];i;i=nxt[i],v=to[i])
#define print(x,y) write(x),putchar(y)

template <class T> inline T read(const T sample) {
	T x=0; int f=1; char s;
	while((s=getchar())>'9'||s<'0') if(s=='-') f=-1;
	while(s>='0'&&s<='9') x=(x<<1)+(x<<3)+(s^48),s=getchar();
	return x*f;
}
template <class T> inline void write(const T x) {
	if(x<0) return (void) (putchar('-'),write(-x));
	if(x>9) write(x/10);
	putchar(x%10^48);
}
template <class T> inline T Max(const T x,const T y) {if(x>y) return x; return y;}
template <class T> inline T Min(const T x,const T y) {if(x<y) return x; return y;}
template <class T> inline T fab(const T x) {return x>0?x:-x;}
template <class T> inline T Gcd(const T x,const T y) {return y?Gcd(y,x%y):x;}
template <class T> inline T Swap(T &x,T &y) {x^=y^=x^=y;}

#include <queue>
using namespace std;

const int N=1e5+5;

int n,m,head[N],cnt,to[N<<1],nxt[N<<1],val[N<<1],in[N],ip[N],k,res[N],ans;
queue <int> q;

void addEdge(int u,int v,int w) {
	nxt[++cnt]=head[u],to[cnt]=v,head[u]=cnt,val[cnt]=w;
}

bool ok(int x) {
	rep(i,1,n) in[i]=0;
	rep(i,1,n) erep(j,i)	
		if(val[j]>x) ++in[v];
	rep(i,1,n) if(!in[i]) q.push(i);
	int tot=0;
	while(!q.empty()) {
		int u=q.front(); q.pop();
		ip[u]=++tot;
		erep(i,u)	
			if(val[i]>x) {
				--in[v];
				if(!in[v]) q.push(v);
			}
	}
	if(tot^n) return 0;
	k=0;
	rep(i,1,n) erep(j,i) {
		if(val[j]>x) continue;
		if(ip[v]<ip[i]) res[++k]=j;
	}
	return 1;
}

int main() {
	int u,v,w,r=0;
	n=read(9),m=read(9);
	rep(i,1,m) {
		u=read(9),v=read(9),w=read(9);
		addEdge(u,v,w);
		r=Max(r,w);
	}
	int l=0,mid;//可能有不选边的情况,虽然不知道为什么答案是 0
	while(l<=r) {
		mid=l+r>>1;
		if(ok(mid)) ans=mid,r=mid-1;
		else l=mid+1;
	}
	print(ans,' '),print(k,'\n');
	rep(i,1,k) print(res[i],' '); puts("");
	return 0;
}
posted on 2020-08-23 09:18  Oxide  阅读(142)  评论(0编辑  收藏  举报