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=;
inline int read(){
int X=,w=; char ch=;
while(!isdigit(ch)) {w|=ch=='-';ch=getchar();}
while(isdigit(ch)) X=(X<<)+(X<<)+(ch^),ch=getchar();
return w?-X:X;
}
struct node{
int w;
int to;
int nxt;
}edge[N*];
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=,g,maxn=n;
q[++r]=st;
fa[st]=;
for(int l=;l<=r;l++){
int u=q[l];
size[u]=;
son[u]=;
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>=;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=,maxp=;
q[++r]=st;
nump[st]=is[st];
dis[st]=L;
fa[st]=;
maxp=max(maxp,nump[st]);
for(int l=;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=;
q[++r]=st;
t[nump[st]]=max(t[nump[st]],dis[st]);
for(int l=;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]=;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=;i<s.size();i++){
getmaxt(s[i].second);
int now=;
if(i){
for(int j=s[i].first;j>=;j--){
while(now+j<k&&now<s[i-].first){
now++;
maxn[now]=max(maxn[now],maxn[now-]);
}
if(now+j<=k)ans=max(ans,maxn[now]+t[j]);
}
}
if(i!=s.size()-){
for(int j=;j<=s[i].first;j++){
maxn[j]=max(maxn[j],t[j]);
t[j]=;
}
}else{
for(int j=;j<=s[i].first;j++){
if(j<=k)ans=max(ans,max(t[j],maxn[j]));
maxn[j]=t[j]=;
}
}
}
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=;i<=m;i++)is[read()]=;
for(int i=;i<n;i++){
int u=read();
int v=read();
int w=read();
add(u,v,w);
add(v,u,w);
}
solve();
printf("%d\n",ans);
return ;
}

SPOJ1825/FTOUR2:Free tour II——包看得懂/看不懂题解的更多相关文章

  1. SPOJ1825 FTOUR2 - Free tour II

    本文版权归ljh2000和博客园共有,欢迎转载,但须保留此声明,并给出原文链接,谢谢合作. 本文作者:ljh2000 作者博客:http://www.cnblogs.com/ljh2000-jump/ ...

  2. SPOJ FTOUR2 - Free tour II

    Description 有些黑点,问你选择不超过 \(k\) 个黑点的路径,路径权值最大是多少. Sol 点分治. 这是qzc的论文题,不过我感觉他的翻译好强啊...我还是选择了自己去看题目... 点 ...

  3. 【SPOJ1825】Free tour II (点分治,启发式)

    题意: 边权可能为负 思路: 感觉我自己写的还是太过僵硬了,可以灵活一点,比如可以多写几个不同的dfs求出不同的信息,而不是压到同一个dfs里 #include<cstdio> #incl ...

  4. FTOUR2 - Free tour II

    传送门 题目翻译的很清楚……似乎点分治的题题目描述都非常简洁. 还是那个操作,一条路径要么全部在一棵子树中,要么经过当前的重心,所以考虑点分治. 首先dfs求出重心的每一棵子树中,有i个黑点的最长路径 ...

  5. SPOJ1825:Free tour II

    题意 luogu的翻译 给定一棵n个点的树,树上有m个黑点,求出一条路径,使得这条路径经过的黑点数小于等于k,且路径长度最大 Sol 点分治辣 如果是等于\(k\)的话,开个桶取\(max\)就好了 ...

  6. [spoj] FTOUR2 FREE TOUR II || 树分治

    原题 给出一颗有n个点的树,其中有M个点是拥挤的,请选出一条最多包含k个拥挤的点的路径使得经过的权值和最大. 正常树分治,每次处理路径,更新答案. 计算每棵子树的deep(本题以经过拥挤节点个数作为d ...

  7. SP1825 FTOUR2 - Free tour II 点分治+启发式合并+未调完

    题意翻译 给定一棵n个点的树,树上有m个黑点,求出一条路径,使得这条路径经过的黑点数小于等于k,且路径长度最大 Code: #include <bits/stdc++.h> using n ...

  8. SP1825 【FTOUR2 - Free tour II】

    # \(SP1825\) 看到没有人用老师的办法,于是自己写一下思路 思路第一步:排除旧方法 首先这道题和\(4178\)不一样,因为那道题是计数,而这道题是求最值,最值有个坏处,就是对于来自相同子树 ...

  9. BZOJ2694:Lcm——包看得懂/看不懂题解

    http://www.lydsy.com/JudgeOnline/problem.php?id=2694 Description 对于任意的>1的n gcd(a, b)不是n^2的倍数 也就是说 ...

随机推荐

  1. memory引擎和innodb引擎速度对比

    ysql> insert into innodb_test (name) select name from innodb_test; Query OK, rows affected ( min ...

  2. Use GitHub Desktop to get GitHub projects

    Find the project's https git file in the home page of the project. e.g. https://github.com/PrismLibr ...

  3. 「题目代码」P1039~P1043(Java)

    P1039 谭浩强C语言(第三版)习题4.9 import java.util.*; import java.io.*; import java.math.BigInteger; public cla ...

  4. 爬虫初体验:Python+Requests+BeautifulSoup抓取广播剧

    可以看到一个DIV下放一个广播剧的信息,包括名称和地址,第一步我们先收集所有广播剧的收听地址: # 用requests的get方法访问novel_list_resp = requests.get(&q ...

  5. 使用 Gradle 配置java项目

    注意点 除非调试,不要print ,否则任务不会按照依赖的顺序执行,因为我们自己喜欢调试用print,但是会打乱执行顺序. 排除测试文件: sourceSets.main.java { srcDir ...

  6. 一:yarn 介绍

        yarn的了出现主要是为了拆分jobtracker的两个核心功能:资源管理和任务监控,分别对应resouceManager(RM)和applicationManager(AM).yarn中的任 ...

  7. 2019寒假训练营寒假作业(二) MOOC的网络空间安全概论笔记部分

    视频课程--MOOC的网络空间安全概论笔记 第一章 网络空间安全概述 2001年,网络空间概念被首次提出: 网络空间安全框架: 1.设备层安全: 可通过截获电磁辐射获取计算机信息.通过硬件木马(恶意电 ...

  8. OnDraw和Opanit的区别

    OnPaint是WM_PAINT消息的消息处理函数,在OnPaint中调用OnDraw,一般来说,用户自己的绘图代码应放在OnDraw中.  OnPaint() 是CWnd的类成员,负责响应WM_PA ...

  9. PAT 1035 插入与归并

    https://pintia.cn/problem-sets/994805260223102976/problems/994805286714327040 据维基百科的定义: 插入排序是迭代算法,逐一 ...

  10. 【Linux】- CentOS 防火墙iptables和firewall

    1 iptables防火墙 1.1 基本操作 # 查看防火墙状态 service iptables status   # 停止防火墙 service iptables stop   # 启动防火墙 s ...