10.7考试总结
二分答案专题OwO
T1
【问题描述】
一场地震毁了 Farmer John 的整个农场。他是个有恒心的人,决定
重建农场。在重建了所有 n( 1<=n<=400)块田野后,他意识到还得
修路将它们连起来。完工后,任两个田野间必须有路。
研究了地形后, FJ 认为 m(1<=m<=10000)条双向的道路可能建造。
由于资金短缺,他希望已尽可能省钱的方式完成整个工程。
幸运的是,奶牛们已经成立了针对地震后修建农场道路的工程顾
问公司。奶牛们也很有经济头脑,对没有漂亮利润的工作从不感兴趣。
奶牛们关心可能的利益。他们已经说定了为修路所获的酬金
f(1<=f<=2,000,000,000),并得到一张关于可能的道路、修建每条路的
时 间 ( 以 小 时 计 ) ( 1 <=t<=2,000,000,000 ) 以 及 花 费
(1<=c<=2000,000,000)的列表。在两块田野间可能有多于一条的道路
被列出,所给数据总有可以连通所有田野的修路方案,虽然可能无利
可图。
确定奶牛修路最高的盈利率。
输入文件
第一行三个整数 N, M, F。
2...M+1 行: 每行四个空格隔开的整数: i, j, c,t 描述两块田夜间的一
条道路。
3
输出文件
只包含一个数,保留四位小数,奶牛每个小时可以得到的最大利润,
如果利润非正,输出 0.0000 。
样例输入 ( quake.in):
5 5 100
1 2 20 5
1 3 20 5
1 4 20 5
1 5 20 5
2 3 23 1
样例输出( quake.out):
1.0625
[奶牛选择建最后四条路,总花费 83 时间 16,他们的筹劳是 100,所
以他们在 16 各单位时间内得到利润 100-83: 17/16 = 1.0625。
网上有 并不知道在哪里评测= =
题目要求\(\frac{F-\sum c_i}{\sum t_i}\)的最大值
令\(x=\frac{F-\sum c_i}{\sum t_i}\),然后乱搞得\(\sum t_i \times x=F-\sum c_i\)
再乱搞得\(F-\sum c_i-\sum t_i\times x=0\)
令\(f(x)=F-\sum c_i-\sum t_i\times x\)
显然这东西是个递减函数= =
二分\(x\),然后把边权设成\(c_i+t_i\times x\),跑一遍最小生成树,把总和与F比一比完事
// It is made by XZZ
#include<cstdio>
#include<algorithm>
#define Fname "quake"
using namespace std;
#define rep(a,b,c) for(rg int a=b;a<=c;a++)
#define drep(a,b,c) for(rg int a=b;a>=c;a--)
#define erep(a,b) for(rg int a=fir[b];a;a=nxt[a])
#define il inline
#define rg register
#define vd void
#define db long double
typedef long long ll;
il int gi(){
rg int x=0;bool flg=0;rg char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-')flg=1;ch=getchar();}
while(ch>='0'&&ch<='9')x=x*10+ch-'0',ch=getchar();
return flg?-x:x;
}
const int maxn=401,maxm=10001;
int n,m,f;
struct edge{int a,b,w,t;db v;}e[maxm];
bool operator < (edge a,edge b){return a.v<b.v;}
int fa[maxn];
il int hd(int i){return fa[i]==i?i:hd(fa[i]);}
il bool check(ll mid){
rep(i,1,m)e[i].v=mid/3e6*e[i].t+e[i].w;
int x=1;
sort(e+1,e+m+1);rep(i,1,n)fa[i]=i;
db k=f+1e-12;
rep(i,2,n){
while(x<=m&&hd(e[x].a)==hd(e[x].b))++x;
fa[hd(e[x].a)]=hd(e[x].b),k-=e[x].v;
if(k<0)return 0;
}return 1;
}
int main(){
freopen(Fname".in","r",stdin);
freopen(Fname".out","w",stdout);
n=gi(),m=gi(),f=gi();
rep(i,1,m)e[i].a=gi(),e[i].b=gi(),e[i].w=gi(),e[i].t=gi();
if(!check(0ll)){puts("0.0000");return 0;}
ll mid,l=0,r=2e15;
while(l<r){
mid=(l+r)>>1;
if(check(mid+1))l=mid+1;
else r=mid;
}printf("%.4Lf\n",l/(db)3e6);
return 0;
}
Ps.mdzz卡精度大狗题。。。
T2
http://www.lydsy.com/JudgeOnline/problem.php?id=1816
二分+1
二分一下几副牌,然后要加的joker数量为\(\sum max(mid-c[i],0)\)
加的joker最多\(min(m,mid)\)个。判断一下下
// It is made by XZZ
#include<cstdio>
#include<algorithm>
#define Fname "cards"
using namespace std;
#define rep(a,b,c) for(rg int a=b;a<=c;a++)
#define drep(a,b,c) for(rg int a=b;a>=c;a--)
#define erep(a,b) for(rg int a=fir[b];a;a=nxt[a])
#define il inline
#define rg register
#define vd void
typedef long long ll;
il ll gi(){
rg ll x=0;bool flg=0;rg char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-')flg=1;ch=getchar();}
while(ch>='0'&&ch<='9')x=x*10+ch-'0',ch=getchar();
return flg?-x:x;
}
ll c[52],n,m;
il bool check(const ll&mid){
ll tot=0;
rep(i,1,n)tot+=max(0ll,mid-c[i]);
return tot<=min(m,mid);
}
int main(){
freopen(Fname".in","r",stdin);
freopen(Fname".out","w",stdout);
n=gi(),m=gi();
ll mid,l=0,r=2e9;
rep(i,1,n)c[i]=gi();
while(l<r){
mid=(l+r)>>1;
if(check(mid+1))l=mid+1;
else r=mid;
}printf("%lld\n",l);
return 0;
}
T3
https://www.luogu.org/problem/show?pid=3199#sub
有向图最小圈。
要求的是\(\frac{\sum C_i}{k}\)最小值
依然令\(x=\frac{\sum C_i}{k}\)
\(k\times x=\sum C_i\)
\(\sum_{i=1}^{k} C_i-k\times x=0\)
\(\sum C_i - x=0\)
如果\(\sum C_i -x <0\)的话,图就有负环了= =二分\(x\),用神搜四把法搞搞。
// It is made by XZZ
#include<cstdio>
#include<algorithm>
#include<cstring>
#define Fname "cycle"
using namespace std;
#define rep(a,b,c) for(rg int a=b;a<=c;a++)
#define drep(a,b,c) for(rg int a=b;a>=c;a--)
#define erep(a,b) for(rg int a=fir[b];a;a=nxt[a])
#define il inline
#define rg register
#define vd void
typedef long long ll;
il int gi(){
rg int x=0;bool flg=0;rg char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-')flg=1;ch=getchar();}
while(ch>='0'&&ch<='9')x=x*10+ch-'0',ch=getchar();
return flg?-x:x;
}
#define db long double
il db gf(){static db a;scanf("%Lf",&a);return a;}
const int maxn=3010,maxm=10010;
int n,m,s,fir[maxn],dis[maxm],nxt[maxm],id;db w[maxm];
il vd add(int a,int b,db c){nxt[++id]=fir[a],dis[id]=b,fir[a]=id,w[id]=c;}
db dist[maxn];bool flg=0;
bool vis[maxn];
il vd spfa(const int &x){
vis[x]=1;
erep(i,x)if(dist[dis[i]]>dist[x]+w[i]){
dist[dis[i]]=dist[x]+w[i];
if(flg||vis[dis[i]]){flg=1;break;}
spfa(dis[i]);
}
vis[x]=0;
}
il bool check(ll a){
rep(i,1,id)w[i]-=a/1e9-(1e-16);
rep(i,1,n)dist[i]=0,vis[i]=0;
flg=0;
rep(i,1,n){
spfa(i);
if(flg)break;
}
rep(i,1,id)w[i]+=a/1e9-(1e-16);
return !flg;
}
int __sum;
il vd dfs(const int&x){vis[x]=1,++__sum;erep(i,x)if(!vis[dis[i]])dfs(dis[i]);}
int main(){
freopen(Fname".in","r",stdin);
freopen(Fname".out","w",stdout);
n=gi(),m=gi();
int a,b;db c;
while(m--)a=gi(),b=gi(),c=gf(),add(a,b,c);
ll l=-1e16,r=1e16,mid;
rep(i,1,n){
memset(vis,0,sizeof vis);
__sum=0;dfs(i);
if(__sum==n){s=i;break;}
}
while(l<r){
mid=((l+r)>>1)+1;
if(check(mid))l=mid;
else r=mid-1;
}
printf("%.8lf\n",l/1e9);
return 0;
}