题意:
就是给你一个树,然后你需要选一些点集,这些点集任何两点只有一个简单路径,说白了就是条链,问你选的点集权值总和/点的个数的最大值或者最少值是多少。
思考:
当时我扫了一眼这题,一看那个公式感觉很复杂就跳过了,主要是过的人也很少,不过交的人挺多,害感觉题目的过题量和交题量太影响自己对题目的判断了。我感觉过的少,应该就没那么简单,看到平均值最大最小,立刻想到了,二分平均值mid,然后01规划树上dp去求最大值和最小值,但是wa掉了。如果eps开的更大一点,又TLE,最后到结束没调出来。我麻了,真折磨啊,这场K也卡精度,这个也卡。然后这个题就是求平均值最大,其实不要想那么复杂啊,如果能选单独一个点的话肯定就选最大的那个或者最小的就行了,这样肯定能保证平均值是最值,以前都在cf上做过,问你平均值最大是多少,不就是选一个或者两个嘛。但是对于这道题,仅仅选两个还是不够的,因为有可能选3个更好,因为这个是拿的一条链嘛,中间的中转点必须也要拿。所以dfs维护maxn1,maxn2,minn1,minn2。枚举拿3个还是2个去更新最大的ans就行了。
md,害,cf最近做少了,思维转化不过来,一直在写算法题,但是区域赛这样玩也没有算法题给你写啊,绝绝。以后要转化好做题方向。思维转化要快一点。
代码:
贪心做法:
int T,n,m,k;
db va[N];
db ans;
vector<int > e[N];
void dfs(int now,int p)
{
db minn1 = inf,minn2 = inf;
db maxn1 = -inf,maxn2 = -inf;
for(auto spot:e[now])
{
if(spot==p) continue;
dfs(spot,now);
if(minn1>=va[spot])
{
minn2 = minn1;
minn1 = va[spot];
}
else minn2 = min(minn2,va[spot]);
if(maxn1<=va[spot])
{
maxn2 = maxn1;
maxn1 = va[spot];
}
else maxn2 = max(maxn2,va[spot]);
ans = max(ans,fabs(va[now]+va[spot])/2.0);
}
if(minn1!=inf&&minn2!=inf)
{
ans = max(ans,fabs(va[now]+minn1+minn2)/3.0);
}
if(maxn1!=inf&&maxn2!=-inf)
{
ans = max(ans,fabs(va[now]+maxn1+maxn2)/3.0);
}
}
signed main()
{
IOS;
cin>>n;
for(int i=1;i<=n;i++) cin>>va[i];
for(int i=1;i<n;i++)
{
int a,b;
cin>>a>>b;
e[a].pb(b);
e[b].pb(a);
}
dfs(1,0);
db anw = ans*ans/4.0;
printf("%.6lf",anw);
return 0;
}
01分数规划(TLE):
#include<bits/stdc++.h>
#define int long long
#define db double
#define pb push_back
#define IOS std::ios::sync_with_stdio(false),cin.tie(0),cout.tie(0);
using namespace std;
const int N = 2e5+5;
const int inf = 1e18;
int n;
db va[N];
db dp[N];
db maxn = -inf;
vector<int > e[N];
void dfs(int now,int p,db mid)
{
dp[now] = va[now]-mid;
for(auto spot:e[now])
{
if(spot==p) continue;
dfs(spot,now,mid);
maxn = max(maxn,dp[now]+dp[spot]);
dp[now] = max(dp[now],va[now]-mid+dp[spot]);
}
if(dp[now]!=va[now]-mid) maxn = max(maxn,dp[now]);
}
bool check(db mid)
{
maxn = -inf;
dfs(1,0,mid);
if(maxn>=0) return true;
return false;
}
void dfs1(int now,int p,db mid)
{
dp[now] = va[now]-mid;
for(auto spot:e[now])
{
if(spot==p) continue;
dfs1(spot,now,mid);
maxn = min(maxn,dp[now]+dp[spot]);
dp[now] = min(dp[now],va[now]-mid+dp[spot]);
}
if(dp[now]!=va[now]-mid) maxn = min(maxn,dp[now]);
}
bool check1(db mid)
{
maxn = inf;
dfs1(1,0,mid);
if(maxn<=0) return true;
return false;
}
signed main()
{
IOS;
cin>>n;
for(int i=1;i<=n;i++) cin>>va[i];
for(int i=1;i<n;i++)
{
int a,b;
cin>>a>>b;
e[a].pb(b);
e[b].pb(a);
}
db l = 0,r = 1e6;
while(r-l>1e-9)
{
db mid = (l+r)/2;
if(check(mid)) l = mid;
else r = mid;
}
db anw = 0;
db ans = l*l/4.0;
anw = max(anw,ans);
l = -1e6,r = 0;
while(r-l>1e-9)
{
db mid = (l+r)/2;
if(check1(mid)) r = mid;
else l = mid;
}
ans = l*l/4.0;
anw = max(anw,ans);
printf("%.6lf",anw);
return 0;
}
总结:
以后多做思维题和构造题和数学题把,算法这个东西,区域赛不像前几年了,现在都是数学场,害,真绝。加油加油。