Description
给出一个n个节点的无根树,每个点有点权。
你要选择一些不相交的路径,如果选择了k条路径,点权和为sum,那么它的价值为
sumk+1
你必须在选择前选择一个数C(0<=c<=T),将所有点权加上C再对limit取模。
求你能收获的最大价值。
N<=10^5,T< limit<=10^6
Solution
昨天才讲完这种题的思路,今天就出了一道题2333
首先可以二分答案啊,然后对于一个mid,判断它是否合法。
就是看
sumk+1>=mid
sum>=mid∗(k+1)
sum−k∗mid>=mid
那么我们只需要将每一条选择的路径的权值减去mid就好了。
这个东西显然可以Dp。
设F[i][0\1]表示i这个点为根的子树中的权值最大值,和一定能往上继续扩展的权值最大值。
这个东西可以通过前缀和做到O(N)
然后考虑枚举这个C
发现如果我们枚举的这个C,使得点权的最大值不是limit-1,那么C+1显然会比C更优。
于是我们枚举每个点权,将其加到limit-1作为一个C值。
这样做的复杂度就降到O(N^2E)了,其中E是二分的复杂度。
不过这样子做的复杂度还是太高,毕竟二分要写实数。
然后题解神奇的随机化了一发~
随机枚举这N个取值,并且在二分之前判断这个取值是否合法
然后复杂度就降下来了?!
不会证明,我是卡常卡过的(雾
Code
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#define fo(i,a,b) for(int i=a;i<=b;i++)
#define fd(i,a,b) for(int i=a;i>=b;i--)
#define rep(i,a) for(int i=last[a];i;i=next[i])
#define max(a,b) (a>b)?a:b
using namespace std;
typedef double db;
const int N=5*1e3+5,M=1e6+5,inf=0x7fffffff;
int n,l,x,y,tot,limit,T,mx,v[N],V[N],w[N],d[N],q[N];
int last[N],next[N*2],t[N*2];
db ans,f[N][2],sum[N],pre[N],eps=1e-7;
bool h[M];
int get() {
char ch;while (!isdigit(ch=getchar()));
int o=ch-48;while (isdigit(ch=getchar())) o=o*10+ch-48;
return o;
}
void add(int x,int y) {
t[++l]=y;next[l]=last[x];last[x]=l;
}
void dfs(int x,int y,db z) {
rep(i,x) if (t[i]!=y) dfs(t[i],x,z);
q[0]=sum[0]=0;pre[0]=-inf;
rep(i,x) if (t[i]!=y) q[++q[0]]=t[i];
fo(i,1,q[0]) sum[i]=sum[i-1]+f[q[i]][1];
fo(i,1,q[0]) pre[i]=max(pre[i-1]+f[q[i]][1],sum[i-1]+f[q[i]][0]);
f[x][1]=sum[q[0]];f[x][0]=sum[q[0]]+v[x]-z;
fo(i,1,q[0]) f[x][0]=max(f[x][0],f[q[i]][0]+sum[q[0]]-f[q[i]][1]+v[x]);
fo(i,1,q[0]) f[x][1]=max(f[x][1],f[q[i]][0]+pre[i-1]+v[x]+z+sum[q[0]]-sum[i]);
f[x][1]=max(f[x][1],f[x][0]);
}
void solve(int x) {
db l=ans,r=5*1e8;
fo(i,1,n) v[i]=(V[i]+x)%limit;
dfs(1,0,ans+eps);
if (max(f[1][0],f[1][1])<ans+eps) return;
while (r-l>eps) {
db mid=(l+r)/2.0;
dfs(1,0,mid);
if (max(f[1][0],f[1][1])>=mid) l=mid;
else r=mid;
}
ans=l;
}
int main() {
n=get();limit=get();
fo(i,1,n) V[i]=get(),V[i]%=limit,mx=max(mx,V[i]);
fo(i,1,n-1) x=get(),y=get(),add(x,y),add(y,x);
T=get();
fo(i,1,n) if (limit-V[i]-1<=T&&!h[limit-V[i]-1]) solve(limit-V[i]-1),h[limit-V[i]-1]=1;
if (mx+T<limit-1) solve(T);
printf("%.6lf\n",ans);
}