牛客挑战赛45 题解&总结
被dyp,gmh77虐爆了。
A
每次找最大的可以删的偶数。直接\(O(n\lg ^2n)\)实现不会TLE。
B
如果一条边两边的子树和都是\(k\)的倍数,这条边一定删。
C
显然改的是一段后缀,枚举后缀算最小代价。然后$z \ xor \ k +k-z \(的规律,如果对应位置上\)k$为\(0\)则\(z\)任选,如果\(k\)为\(1\)则\(z\)选\(0\)有\(2\)的代价,选\(1\)无代价。按照二进制从高到低决定每个位。
D
\(T2\)随机,子树大小期望是\(\frac{1}{n}\sum siz_i=\frac{1}{n}\sum dep_i=O(\lg n)\)。
暴力枚举修改哪些点,在\(T1\)中用数据结构维护一下。我写了点分树。
题解做法更简单:直接维护直径端点,修改的时候旧直径至少一个端点是新直径的端点。
using namespace std;
#include <cstdio>
#include <cstring>
#include <algorithm>
#define N 100005
#define ll long long
#define mp(x,y) make_pair(x,y)
int n;
struct EDGE{
int to,w;
EDGE *las;
int bz;
};
struct Graph{
EDGE e[N*2];
EDGE *rev(EDGE *ei){return (e+((ei-e)^1));}
int ne;
EDGE *last[N];
void link(int u,int v,int w){
e[ne]={v,w,last[u],N};
last[u]=e+ne++;
}
} S,T;
ll ans;
int fa2[N];
ll dep2[N];
void initT(int x){
for (EDGE *ei=T.last[x];ei;ei=ei->las)
if (ei->to!=fa2[x]){
fa2[ei->to]=x;
dep2[ei->to]=dep2[x]+ei->w;
initT(ei->to);
}
}
int mxd[N];
#define forei(x) for (EDGE *(ei)=S.last[x];ei;ei=ei->las) if (ei->to!=fa && ei->bz>=d)
int siz[N],all;
void getsiz(int x,int fa,int d){
siz[x]=1;
forei(x){
getsiz(ei->to,x,d);
siz[x]+=siz[ei->to];
}
// printf("%d %d %d\n",d,x,siz[x]);
}
int getG(int x,int fa,int d){
int is=(all-siz[x]<=all>>1);
forei(x){
int t=getG(ei->to,x,d);
if (t) return t;
is&=(siz[ei->to]<=all>>1);
}
return is?x:0;
}
ll dis[18][N],mx[18][N];
int bel[18][N],rt[18][N];
pair<pair<ll,int>,pair<ll,int> > opt[N];
void upd(pair<pair<ll,int>,pair<ll,int> > &a,pair<ll,int> b){
if (b>a.first)
a.second=a.first,a.first=b;
else if (b>a.second)
a.second=b;
}
void getdis(int x,int fa,int d){
mx[d][x]=dis[d][x]+dep2[x];
forei(x){
dis[d][ei->to]=dis[d][x]+ei->w;
if (fa==0){
bel[d][ei->to]=ei->to;
rt[d][ei->to]=x;
}
else{
bel[d][ei->to]=bel[d][x];
rt[d][ei->to]=rt[d][x];
}
getdis(ei->to,x,d);
mx[d][x]=max(mx[d][x],mx[d][ei->to]);
}
}
void divide(int x,int d){
getsiz(x,0,d),all=siz[x];
x=getG(x,0,d);
mxd[x]=d;
bel[d][x]=rt[d][x]=x;
dis[d][x]=0,getdis(x,0,d);
opt[x]=mp(mp(dep2[x],x),mp(dep2[x],x));
for (EDGE *ei=S.last[x];ei;ei=ei->las)
if (ei->bz>=d)
upd(opt[x],mp(mx[d][ei->to],ei->to));
ans=max(ans,opt[x].first.first+opt[x].second.first);
for (EDGE *ei=S.last[x];ei;ei=ei->las)
if (ei->bz>=d){
S.rev(ei)->bz=ei->bz=d;
divide(ei->to,d+1);
}
}
void change(int x,int k){
dep2[x]+=k;
for (int i=mxd[x];i>=1;--i){
pair<ll,int> tmp=mp(dis[i][x]+dep2[x],bel[i][x]);
pair<pair<ll,int>,pair<ll,int> > &o=opt[rt[i][x]];
if (o.first.second==tmp.second)
o.first=max(o.first,tmp);
else if (o.second.second==tmp.second){
if (tmp>o.second){
o.second=tmp;
if (o.first<o.second)
swap(o.first,o.second);
}
}
else
upd(o,tmp);
ans=max(ans,o.first.first+o.second.first);
}
}
void dfs(int x,int k){
// printf("%d %d\n",x,k);
change(x,k);
for (EDGE *ei=T.last[x];ei;ei=ei->las)
if (ei->to!=fa2[x])
dfs(ei->to,k);
}
int main(){
int Q;
scanf("%d%d",&n,&Q);
for (int i=1;i<n;++i){
int u,v,w;
scanf("%d%d%d",&u,&v,&w);
S.link(u,v,w);
S.link(v,u,w);
}
for (int i=1;i<n;++i){
int u,v,w;
scanf("%d%d%d",&u,&v,&w);
T.link(u,v,w);
T.link(v,u,w);
}
initT(1);
divide(1,1);
// for (int i=1;i<=n;++i)
// printf("%d ",mxd[i]);
// printf("\n");
printf("%lld\n",ans);
while (Q--){
int x,k;
scanf("%d%d",&x,&k);
dfs(x,k);
printf("%lld\n",ans);
}
return 0;
}
E
https://www.cnblogs.com/jz-597/p/13972201.html
F
待填坑
G
https://www.cnblogs.com/jz-597/p/13977888.html
最大的败笔是想不出E……
总是在考虑每次修改之后连通块的变化,而不是最后连通块的情况。
正难则反,这种基本的思想都掌握不好呢。