SPOJ1825/FTOUR2:Free tour II——包看得懂/看不懂题解
http://www.spoj.com/problems/FTOUR2/en/
题目大意:给一棵黑白染色的树,求边权和最大且经过黑点不超过K的路径。
————————————————————
前排膜拜hzwer,借(抄)鉴(袭)了神犇的代码与思路,要看简洁的思路的话:http://hzwer.com/5984.html
然而……就算是抄了代码,还是没懂这题怎么做怎么办?
别着急,这道神神神题慢慢来。
——————————————————————
首先判断我们要放什么板子,很显然我们想到的是和树有关的算法,全拎出来一遍发现点分治貌似可行?
那我们点分治就直接求就好了!
e
然后跟着下面的代码慢慢剖析(与程序有些不符):
1.calcg函数:和普通板子一样,是找重心的。
2.getmaxp函数:处理子树,返回当前子树节点到子树根的路径中最多黑点个数,同时我们主要完成下面数组的更新:
1.nump[i]:统计i到根节点的路径上的黑点数。
2.dis[i]:统计i到根节点的路径权值和。
3.getmaxt函数:处理子树的t数组,其中t数组含义如下:
t[i]:当前子树节点到子树根且经过至多i个黑点的路径的最大权值和。
4.solve函数:处理答案ans,过程如下:
1.calcg得到重心作为根节点。
2.getmaxp每个子树的根节点,并且放入我们准备好的s数组里(同时存入子树的根,用make_pair函数更加便捷)。
3.sort s数组。//原因见4.4
4.遍历s数组:
0.初始化t数组//请将该操作放在下面和循环一起进行,不然会卡常数导致TLE
1.getmaxt子树根。
2.处理maxn数组,并且更新ans,maxn数组含义如下:
maxn[i]:以根节点为起点,终点在前面已经搜过的子树中,且该路径至多经过i个黑点,这样的路径的最大权值和。
我们令当前子树经过的黑点个数为j,前面的子树经过的黑点个数为now,我们显然有:
maxn[now]=max(maxn[now],maxn[now-1]);
我们同样显然有:
if(now+j<=k)ans=max(ans,maxn[now]+t[j]);
但是显然我们的now不可能全跑一遍(TLE预定),所以我们有两种优化:
1.now<=s[i-1].first(显然,我们因为sort了所以我们知道now最大不超过s[i-1].first)
2.j从s[i].first搜到0,期间保证now+j<=k(原因:now越大maxn数组越大所更新的ans越大,所以我们最初就把now放到最大,一来我们更新完了maxn可以不用再更新了,二来此时的maxn[now]+t[j]一定是当前状态最优解。
咱们继续更新maxn(为了下一步的maxn)
当当前子树不是最后一棵子树的时候(显然如果是最后一棵的话还更新什么?)显然这么更新即可:maxn[j]=max(maxn[j],t[j]);
当当前子树是最后一棵子树的时候,我们扫尾将每个子树节点到根节点的距离更新进ans。
5.删掉重心,递归上述过程。
——————————————————————————————————
好的代码讲解就此结束,最后吐槽一句吧这题卡!常!数!所以把它推向神坛,不然还加那么多奇葩优化干什么……
#include<cmath> #include<cstdio> #include<queue> #include<cctype> #include<cstring> #include<vector> #include<algorithm> using namespace std; typedef long long ll; typedef pair<int,int>pii; const int N=200001; inline int read(){ int X=0,w=0; char ch=0; while(!isdigit(ch)) {w|=ch=='-';ch=getchar();} while(isdigit(ch)) X=(X<<3)+(X<<1)+(ch^48),ch=getchar(); return w?-X:X; } struct node{ int w; int to; int nxt; }edge[N*2]; vector<pair<int,int> >s; int cnt,n,m,k,head[N],q[N],size[N],son[N],nump[N],fa[N],maxn[N],t[N],ans,d[N],dis[N]; bool vis[N],is[N]; void add(int u,int v,ll w){ cnt++; edge[cnt].to=v; edge[cnt].w=w; edge[cnt].nxt=head[u]; head[u]=cnt; return; } int calcg(int st){ int r=0,g,maxn=n; q[++r]=st; fa[st]=0; for(int l=1;l<=r;l++){ int u=q[l]; size[u]=1; son[u]=0; for(int i=head[u];i;i=edge[i].nxt){ int v=edge[i].to; if(vis[v]||v==fa[u])continue; fa[v]=u; q[++r]=v; } } for(int l=r;l>=1;l--){ int u=q[l],v=fa[u]; if(r-size[u]>son[u])son[u]=r-size[u]; if(son[u]<maxn)g=u,maxn=son[u]; if(!v)break; size[v]+=size[u]; if(size[u]>son[v])son[v]=size[u]; } return g; } inline int getmaxp(int st,ll L){ int r=0,maxp=0; q[++r]=st; nump[st]=is[st]; dis[st]=L; fa[st]=0; maxp=max(maxp,nump[st]); for(int l=1;l<=r;l++){ int u=q[l]; for(int i=head[u];i;i=edge[i].nxt){ int v=edge[i].to; int w=edge[i].w; if(vis[v]||v==fa[u])continue; fa[v]=u; dis[v]=dis[u]+w; nump[v]=nump[u]+is[v]; maxp=max(maxp,nump[v]); q[++r]=v; } } return maxp; } inline void getmaxt(int st){ int r=0; q[++r]=st; t[nump[st]]=max(t[nump[st]],dis[st]); for(int l=1;l<=r;l++){ int u=q[l]; for(int i=head[u];i;i=edge[i].nxt){ int v=edge[i].to; int w=edge[i].w; if(vis[v]||v==fa[u])continue; t[nump[v]]=max(t[nump[v]],dis[v]); q[++r]=v; } } return; } void solve(int u){ int g=calcg(u); vis[g]=1;s.clear(); for(int i=head[g];i;i=edge[i].nxt){ int v=edge[i].to; int w=edge[i].w; if(!vis[v])s.push_back(pii(getmaxp(v,w),v)); } sort(s.begin(),s.end()); if(is[g])k--; for(int i=0;i<s.size();i++){ getmaxt(s[i].second); int now=0; if(i){ for(int j=s[i].first;j>=0;j--){ while(now+j<k&&now<s[i-1].first){ now++; maxn[now]=max(maxn[now],maxn[now-1]); } if(now+j<=k)ans=max(ans,maxn[now]+t[j]); } } if(i!=s.size()-1){ for(int j=0;j<=s[i].first;j++){ maxn[j]=max(maxn[j],t[j]); t[j]=0; } }else{ for(int j=0;j<=s[i].first;j++){ if(j<=k)ans=max(ans,max(t[j],maxn[j])); maxn[j]=t[j]=0; } } } if(is[g])k++; for(int i=head[g];i;i=edge[i].nxt){ int v=edge[i].to; if(!vis[v])solve(v); } return; } int main(){ n=read(); k=read(); m=read(); for(int i=1;i<=m;i++)is[read()]=1; for(int i=1;i<n;i++){ int u=read(); int v=read(); int w=read(); add(u,v,w); add(v,u,w); } solve(1); printf("%d\n",ans); return 0; }