传送门

这题考试的时候觉得时间复杂度假了,\(n \geqslant 1000\)的部分直接瞎写了个特殊性质上去,结果假的时间复杂度能有60pts……

  • 比较大的数组无论如何不要直接全部memset!如果在写部分分,考虑用多少memset多少

    memset真的可以把一份74pts代码卡成30pts的 memset一个1e9的int数组要0.2s long long要0.7s

首先70pts可以枚举起点,每次跑一遍dfs

令 \(g[i][j]\) 表示以 \(i\) 为起点,撒 \(j\) 次面包屑得到的最大收益即可

这部分代码:

#include <bits/stdc++.h>
using namespace std;
#define INF 0x3f3f3f3f
#define N 100010
#define ll long long
#define ld long double
#define usd unsigned
#define ull unsigned long long
//#define int long long #define getchar() (p1==p2&&(p2=(p1=buf)+fread(buf, 1, 1<<21, stdin)), p1==p2?EOF:*p1++)
char buf[1<<21], *p1=buf, *p2=buf;
inline int read() {
int ans=0, f=1; char c=getchar();
while (!isdigit(c)) {if (c=='-') f=-f; c=getchar();}
while (isdigit(c)) {ans=(ans<<3)+(ans<<1)+(c^48); c=getchar();}
return ans*f;
} int n, V;
int p[N], head[N], size;
struct edge{int to, next; bool vis;}e[N<<1];
inline void add(int s, int t) {edge* k=&e[++size]; k->to=t; k->next=head[s]; head[s]=size;} namespace force{
ll ans;
bool none[N];
void dfs(int u, int fa, int v2, ll sum) {
if (v2<=0) {ans=max(ans, sum); return ;}
bool cge=0;
if (!none[u]) none[u]=1, cge=1;
for (int i=head[u],v; i; i=e[i].next) {
v = e[i].to;
if (v!=fa) dfs(v, u, v2, sum);
}
for (int i=head[u],v; i; i=e[i].next) if (!none[e[i].to]) {sum+=p[e[i].to]; none[e[i].to]=1; e[i].vis=1;}
for (int i=head[u],v; i; i=e[i].next) {
v = e[i].to;
if (v!=fa) dfs(v, u, v2-1, sum);
}
for (int i=head[u],v; i; i=e[i].next) if (e[i].vis) {e[i].vis=0; none[e[i].to]=0;}
if (cge) none[u]=0;
}
void solve() {
//for (int i=1; i<=n; ++i) {
//memset(none, 0, sizeof(none));
dfs(1, 0, V, 0);
//}
printf("%lld\n", ans);
exit(0);
}
} namespace task1{
ll dp[N][105][4], ans;
//ll allcnt;
void dfs(int u, int fa) {
//cout<<"dfs "<<u<<' '<<fa<<endl;
ll sum=0;
bool leaf=1;
for (int i=head[u]; i; i=e[i].next) {
sum+=p[e[i].to];
if (e[i].to!=fa) leaf=0;
}
memset(dp[u], 0, sizeof(ll)*420);
dp[u][V][3]=p[fa];
if (leaf) return ;
for (int i=head[u],v; i; i=e[i].next) {
v = e[i].to;
if (v!=fa) {
dfs(v, u);
for (int s=V; s>=0; --s) {
//++allcnt;
//dp[u][s][0]=dp[u][s][1]=dp[u][s][2]=dp[u][s][3]=0;
dp[u][s][0] = max(dp[u][s][0], max(dp[v][s+1][2], dp[v][s+1][3]));
dp[u][s][1] = max(dp[u][s][1], max(dp[v][s][0], dp[v][s][1]));
if (s>0) {
dp[u][s][2] = max(dp[u][s][2], sum-p[v]+max(dp[v][s+1][2], dp[v][s+1][3]));
dp[u][s][3] = max(max(dp[u][s][3], sum-p[v]+max(dp[v][s][0], dp[v][s][1])), sum);
}
}
}
}
}
void solve() {
//cout<<double(sizeof(dp))/1024/1024<<endl;
for (int i=1; i<=n; ++i) {
//cout<<i<<endl;
if (clock()>=1600000) {printf("%lld\n", ans); exit(0);}
//memset(dp, 0, sizeof(dp));
dfs(i, 0);
for (int j=0; j<=V; ++j) ans=max(ans, max(max(dp[i][j][0], dp[i][j][1]), max(dp[i][j][2], dp[i][j][3])));
}
//int rt=2;
//dfs(rt, 0);
//for (int j=0; j<=V; ++j) ans=max(ans, max(max(dp[rt][j][0], dp[rt][j][1]), max(dp[rt][j][2], dp[rt][j][3])));
#if 0
for (int i=1; i<=n; ++i) {
for (int j=0; j<=V; ++j) {
for (int k=0; k<4; ++k) cout<<dp[i][j][k]<<' '; cout<<endl;
}
}
#endif
//for (int j=0; j<=V; ++j) {for (int k=0; k<4; ++k) cout<<dp[2][j][k]<<' '; cout<<endl;}
printf("%lld\n", ans);
//cout<<"allcnt: "<<allcnt<<endl;
exit(0);
}
} namespace task2{
ll f[N][105], g[N][105], ans;
void dfs(int u, int fa) {
//cout<<"dfs "<<u<<' '<<fa<<endl;
ll sum=0;
for (int i=head[u]; i; i=e[i].next) sum+=p[e[i].to];
memset(f[u], 0, sizeof(ll)*105);
f[u][1]=sum;
for (int i=head[u],v; i; i=e[i].next) {
v = e[i].to;
if (v==fa) continue;
memset(g[v], 0, sizeof(ll)*105);
//for (int j=1; j<=V; ++j) {
// g[v][j] = max(g[v][j], max(g[u][j], sum-p[fa]+g[u][j-1]));
//}
dfs(v, u);
for (int j=1; j<=V; ++j) {
//f[u][j] = max(f[u][j], max(f[v][j], sum-p[v]+f[v][j-1]));
g[u][j] = max(g[u][j], max(g[v][j], sum-p[fa]+g[v][j+1]));
}
}
}
void solve() {
for (int i=1; i<=n; ++i) {
if (clock()>=1600000) {printf("%lld\n", ans); exit(0);}
memset(g[i], 0, sizeof(ll)*105);
dfs(i, 0);
for (int j=1; j<=n; ++j) for (int k=0; k<=V; ++k) ans=max(ans, max(f[j][k], g[j][k]));
//for (int j=0; j<=V; ++j) cout<<g[3][j]<<' '; cout<<endl;
}
printf("%lld\n", ans);
exit(0);
}
} namespace task{
ll f[N][105], g[N][105], ans;
void dfs(int u, int fa) {
//cout<<"dfs "<<u<<' '<<fa<<endl;
ll sum=0;
for (int i=head[u]; i; i=e[i].next) sum+=p[e[i].to];
f[u][1]=sum;
ll maxn[105][4], maxi[105][4];
memset(maxn, 0, sizeof(maxn));
memset(maxi, 0, sizeof(maxi));
for (int i=head[u],v; i; i=e[i].next) {
v = e[i].to;
if (v==fa) continue;
for (int j=1; j<=V; ++j) {
g[v][j] = max(g[v][j], max(g[u][j], sum-p[fa]+g[u][j-1]));
if (g[v][j]>=maxn[j][0]) {maxn[j][1]=maxn[j][0]; maxi[j][1]=maxi[j][0]; maxn[j][0]=g[v][j]; maxi[j][0]=v;}
else if (g[v][j]>maxn[j][1]) maxn[j][1]=g[v][j], maxi[j][1]=v;
}
dfs(v, u);
for (int j=1; j<=V; ++j) {
f[u][j] = max(f[u][j], max(f[v][j], sum-p[v]+f[v][j-1]));
if (f[v][j]>=maxn[j][2]) {maxn[j][3]=maxn[j][2]; maxi[j][3]=maxi[j][2]; maxn[j][2]=f[v][j]; maxi[j][2]=v;}
else if (f[v][j]>maxn[j][3]) maxn[j][3]=f[v][j], maxi[j][3]=v;
}
}
cout<<"u: "<<u<<endl;
for (int j=0; j<V; ++j) {
if (maxi[j+1][0]!=maxi[j][2]) ans=max(ans, maxn[j+1][0]+maxn[j][2]), cout<<"try1: "<<maxn[j+1][0]<<' '<<maxi[j+1][0]<<' '<<maxn[j][2]<<' '<<maxi[j][2]<<' '<<maxn[j+1][0]+maxn[j][2]<<endl;
else {
if (maxi[j+1][1]!=maxi[j][2]) ans=max(ans, maxn[j+1][1]+maxn[j][2]), cout<<"try2: "<<maxn[j+1][1]<<' '<<maxi[j+1][1]<<' '<<maxn[j][2]<<' '<<maxi[j][2]<<' '<<maxn[j+1][1]+maxn[j][2]<<endl;
if (maxi[j+1][0]!=maxi[j][3]) ans=max(ans, maxn[j+1][0]+maxn[j][3]), cout<<"try3: "<<maxn[j+1][0]<<' '<<maxn[j][3]<<' '<<maxn[j+1][0]+maxn[j][3]<<endl;
}
ans=max(ans, max(maxn[j][0], maxn[j][2]));
}
}
void solve() {
dfs(1, 0);
printf("%lld\n", ans);
exit(0);
}
} signed main()
{
#ifdef DEBUG
freopen("1.in", "r", stdin);
#endif n=read(); V=read();
for (int i=1; i<=n; ++i) p[i]=read();
for (int i=1,u,v; i<n; ++i) {
u=read(); v=read();
add(u, v); add(v, u);
}
task2::solve(); return 0;
}

然后考虑如何不枚举起点

那就需要换根DP了,令 \(f[i][j]\) 表示从以i为根的子树中走到 \(i\) ,$ g[i][j]$ 表示从i的父亲走到 \(i\) 及其子树中撒 \(j\) 次的最大收益

转移的时候要特别注意先后顺序

首先方程有了,ans=max(ans, f[u][j]+g[to][v-j])

而我们要在同一次遍历中更新 \(ans,f[u][j],g[u][j]\)

因为f和g肯定不能选来自同一棵子树的,所以f要用从之前遍历过的子树中的,所以先更新ans,再转移f,g

发现这样只是在用一个g匹配它左边的所有f,显然不够,所以还要逆序枚举一遍

挺有思维量的,做了巨久……还因为变量名重了没看出来陷入高度自闭

Code:

#include <bits/stdc++.h>
using namespace std;
#define INF 0x3f3f3f3f
#define N 100010
#define ll long long
//#define int long long char buf[1<<21], *p1=buf, *p2=buf;
#define getchar() (p1==p2&&(p2=(p1=buf)+fread(buf, 1, 1<<21, stdin)), p1==p2?EOF:*p1++)
inline int read() {
int ans=0, f=1; char c=getchar();
while (!isdigit(c)) {if (c=='-') f=-f; c=getchar();}
while (isdigit(c)) {ans=(ans<<3)+(ans<<1)+(c^48); c=getchar();}
return ans*f;
} int n, v;
int head[N], size, sta[N], top; ll p[N], sum[N], ans, f[N][105], g[N][105];
struct edge{int to, next;}e[N<<1];
inline void add(int s, int t) {edge* k=&e[++size]; k->to=t; k->next=head[s]; head[s]=size;} void dfs(int u, int fa) {
for (int i=head[u],v; i; i=e[i].next) {
v = e[i].to;
sum[u]+=p[v];
if (v!=fa) dfs(v, u);
}
int to;
for (int i=1; i<=v; ++i) f[u][i]=sum[u], g[u][i]=sum[u]-p[fa];
for (int i=head[u]; i; i=e[i].next) {
to = e[i].to;
if (to==fa) continue;
sta[++top]=to;
for (int j=1; j<=v; ++j) {
ans = max(ans, f[u][j]+g[to][v-j]);
f[u][j]=max(f[u][j], max(f[to][j], f[to][j-1]+sum[u]-p[to]));
g[u][j]=max(g[u][j], max(g[to][j], g[to][j-1]+sum[u]-p[fa]));
}
}
ans = max(ans, max(f[u][v], g[u][v]));
for (int i=1; i<=v; ++i) f[u][i]=sum[u], g[u][i]=sum[u]-p[fa];
while (top) {
to=sta[top--];
for (int j=1; j<=v; ++j) {
ans = max(ans, f[u][j]+g[to][v-j]);
f[u][j]=max(f[u][j], max(f[to][j], f[to][j-1]+sum[u]-p[to]));
g[u][j]=max(g[u][j], max(g[to][j], g[to][j-1]+sum[u]-p[fa]));
}
}
ans = max(ans, max(f[u][v], g[u][v]));
} signed main()
{
n=read(); v=read();
for (int i=1; i<=n; ++i) p[i]=read();
for (int i=1,u,v; i<n; ++i) {u=read(); v=read(); add(u, v); add(v, u);}
dfs(1, 0);
printf("%lld\n", ans); return 0;
}

题解 park/chase的更多相关文章

  1. [考试反思]1007csp-s模拟测试63:朦胧

    别找了原来没有写过叫<朦胧>的我check过了.(慌的一匹) 总算是比较早的改完了一套题. 但是考的是个啥啊... 前两道题都很卡常导致我想到了正解但是都放弃了. 2e8的复杂度怎么可能能 ...

  2. 题解报告:hdu 4607 Park Visit(最长链)

    Problem Description Claire and her little friend, ykwd, are travelling in Shevchenko's Park! The par ...

  3. 洛谷 AT2434 JOI 公園 (JOI Park) 题解

    人生第一次AC黑题,我太感动了. 每日一题 day31 打卡 Analysis 先跑遍DJ,求出1到 i的最短路.得到每个点到 1号点的距离后,从小到大排序一遍,这时便可以枚举每个点到 1号点的距离修 ...

  4. Codeforces Round #292 (Div. 1) C. Drazil and Park 线段树

    C. Drazil and Park 题目连接: http://codeforces.com/contest/516/problem/C Description Drazil is a monkey. ...

  5. UVALive 4221 Walk in the Park 扫描线

    Walk in the Park 题目连接: https://icpcarchive.ecs.baylor.edu/index.php?option=com_onlinejudge&Itemi ...

  6. Codeforces Round #321 (Div. 2) C. Kefa and Park dfs

    C. Kefa and Park Time Limit: 1 Sec Memory Limit: 256 MB 题目连接 http://codeforces.com/contest/580/probl ...

  7. chd校内选拔赛题目+题解

    题目链接   A. Currency System in Geraldion 有1时,所有大于等于1的数都可由1组成.没有1时,最小不幸的数就是1. #include<iostream> ...

  8. 【CF679D】Bear and Chase 最短路+乱搞

    [CF679D]Bear and Chase 题意:近日,鼠国的头号通缉犯,神出鬼没的怪盗——Joker正于摩登市出没!对于名侦探Jack来说,这正是将其捉拿归案的大号时机.形式化地,摩登市可以看成一 ...

  9. UVALive 6889 City Park 并查集

    City Park 题目连接: http://acm.hust.edu.cn/vjudge/contest/view.action?cid=122283#problem/F Description P ...

随机推荐

  1. Leetcode No.66 Plus One(c++实现)

    1. 题目 1.1 英文题目 Given a non-empty array of decimal digits representing a non-negative integer, increm ...

  2. ADC电阻分压采集相关知识

    1.电池串联可以增加电压,电池并联可以增加电流,都是为了增加电功率.但是电池不宜并联,即使是相同规格的电池,如果消耗不同,电势就会不同,会造成电池之间的放电现象. 2.电阻的作用: 限流:为使通过用电 ...

  3. java基础---数组的基本概念(1)

    学习资源来自尚硅谷java基础学习 1. 数组的概念 数组(Array), 是多个相同类型数据按一定顺序排列的集合, 并使用一个名字命名, 并通过编号的方式对这些数据进行统一管理. 数组属于引用数据类 ...

  4. Python使用笔记20--网络操作小练习

    1 ''' 2 2.自己抓取qq群的接口,传入一个群号,然后把群成员的头像下载到本地,头像用群备注来命名,如果没有 3 群备注,那么取昵称. 4 ''' 5 import requests 6 imp ...

  5. C++11 左值引用和右值引用与引用折叠和完美转发

    1.左值与右值 最感性的认识. 当然,左值也是可以在右边的. 左值是可以被修改的,右值不能. 当然取地址也是. 生存周期一般左值会比右值的长,一般右值都计算时产生的无名临时对象,存在时间比较短. 下面 ...

  6. Redis学习——简介

    一.Redis简介 Redis是一种基于键值对(key-value)的NoSQL数据库,与很多键值对数据库不同的是,Redis中的值可以是由string(字符串).hash(哈希).list(列表). ...

  7. P7324 [WC2021] 表达式求值

    P7324 [WC2021] 表达式求值 闲话 WC2021 我只得了 20 分,三道题总共 20 分.我是下场了突然后知后觉这件事的,主要原因是我开了 C++11,然后 T1 T2 都没分了.在洛谷 ...

  8. 开发工具IDE从入门到爱不释手(六)常用插件Git

    Git 环境准备 本地基本操作 本地文件关联git管理 文件提交git Ctrl+K:提交 关联远程仓库 先注册github账号 连接本地 git菜单 撤销提交 追加提交 Code Review

  9. Cesium加载地形数据只显示半个地球

    Cesium第0级地形包括两个瓦片:0/0/0.terrain,0/1/0.terrain,分别为左半球和右半球(具体参考:https://blog.csdn.net/u013929284/artic ...

  10. 第4天 JavaDoc生成文档&Java流程控制(第一节:用户交互Scanner)

    JavaDoc生成文档 javadoc命令是用来生成自己的API文档 参数信息: @author 作者名 @version 版本号 @since 指明需要最早使用的jdk版本 @param 参数名 @ ...