牛客挑戰賽45 題解&總結
阿新 • • 發佈:2020-11-15
被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……
總是在考慮每次修改之後連通塊的變化,而不是最後連通塊的情況。
正難則反,這種基本的思想都掌握不好呢。