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;
}

 

posted @ 2017-12-14 11:33  luyouqi233  阅读(438)  评论(0编辑  收藏  举报