• 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
    ",ans);
        return 0;
    }
  • 相关阅读:
    unittest单元测试框架之unittest工作原理(一)
    unittest单元测试框架之unittest案例(二)
    mysql 查询导出(txt,csv,xls)
    JS 无限长form表单提交
    PHP设计模式的六大设计原则
    MySql 双主多从配置指导
    MySQL5.7开多实例指导
    MySQL主从复制配置指导及PHP读写分离源码分析
    《单元测试之道Java版》的读书笔记
    《重构》的读书笔记–方法列表
  • 原文地址:https://www.cnblogs.com/luyouqi233/p/8036828.html
Copyright © 2020-2023  润新知