【题解】cycle
题目描述
给定一个无向图,求一个环,使得环内边权(div)环内点数最大。
数据范围
(n le 5000) (mle 10000)
(Solution)
考虑到我们可以对答案的式子变一下形,
(frac{Sigma_{iin V'} w_i}{|V'|}le ans)
(Sigma_{iin V'}w_i-ans imes |V'|le0)
这一步不要看不懂了((i)共有(|V'|)个,所以(ans)仍然总共被加了(|V'|)次):
(Sigma_{iin V'} (w_i-ans) le 0)
即
(Sigma_{i in V'} w'_ile0)
直接(spfa)跑负环就好了。
实际上这是一个很有用的方法,先根据题目答案的意义判断是否存在答案单调性,再通过数学变换得到我们想要的式子。
或者感性理解,我们得到一堆边的平均边权是(ans)了,那么这个边集的所有权减去这个(ans)然后加起来一定等于(0)。假如加起来比(0)大,说明不存在,假设比这个小,说明有更优解。
上好看的代码qvq
#include<bits/stdc++.h>
using namespace std;typedef long long ll;
#define DRP(t,a,b) for(register int t=(a),edd=(b);t>=edd;--t)
#define RP(t,a,b) for(register int t=(a),edd=(b);t<=edd;++t)
#define ERP(t,a) for(register int t=head[a];t;t=e[t].nx)
#define midd register int mid=(l+r)>>1
#define TMP template < class ccf >
TMP inline ccf qr(ccf b){
register char c=getchar();register int q=1;register ccf x=0;
while(c<48||c>57)q=c==45?-1:q,c=getchar();
while(c>=48&&c<=57)x=x*10+c-48,c=getchar();
return q==-1?-x:x;}
TMP inline ccf Max(ccf a,ccf b){return a<b?b:a;}
TMP inline ccf Min(ccf a,ccf b){return a<b?a:b;}
TMP inline ccf Max(ccf a,ccf b,ccf c){return Max(a,Max(b,c));}
TMP inline ccf Min(ccf a,ccf b,ccf c){return Min(a,Min(b,c));}
TMP inline ccf READ(ccf* _arr,int _n){RP(t,1,_n)_arr[t]=qr((ccf)1);}
//----------------------template&IO---------------------------
const int maxn=3e3+15;
const int maxm=1e4+15;
struct E{
int to,nx;
long double w;
}e[maxm];
const long double EPS=1e-10;
int head[maxn];
int usd[maxn];
bool in[maxn];
double d[maxn];
double sa[maxm];
int cnt;
int n,m;
inline void add(int fr,int to,long double w){
cnt++;
e[cnt].to=to;
e[cnt].nx=head[fr];
e[cnt].w=w;
sa[cnt]=w;
head[fr]=cnt;
}
bool c=0;
void spfa(int now){
if(c) return;
usd[now]=1;
ERP(t,now){
if(d[e[t].to]>d[now]+e[t].w){
d[e[t].to]=d[now]+e[t].w;
if(c||usd[e[t].to]) return void(c=1);
spfa(e[t].to);
}
}
usd[now]=0;
}
inline bool chek(long double x){
RP(t,1,m) e[t].w=sa[t]-x;
RP(t,1,n) usd[t]=d[t]=0;c=0;
RP(t,1,n) if(!usd[t]) spfa(t);
return c;
}
int main(){
n=qr(1);m=qr(1);
int t1,t2;
long double t3;
long double l=-1e7-(long double)1,r=1e7+(long double)1;
long double mid;
RP(t,1,m){
t1=qr(1);t2=qr(1);
scanf("%Lf",&t3);
add(t1,t2,t3);
}
do{
mid=(l+r)/(long double)2;
if(chek(mid))
r=mid;
else
l=mid;
}while(l+EPS<r);
printf("%.8Lf
",l);
return 0;
}
/*
dfs序+树连剖腹
乱做
orz yyb
不行 有情况没有考虑到!
直接tarjin
二分就好了吧 乱做OK
不行 我是****
*/