CF#196B http://codeforces.com/contest/338/problem/B

题意:在一颗树上,给m个点,求到所有m个点距离不超过d的点的个数,所有路径长度为1。

分析:问题转化,就是求一个点是否到离它最远的给定点的距离小于d,

先第一遍dfs1求出以当前节点u为根的子树到子树中离它最远的给定点的距离d1[u],和次远的距离d2[u],

并且记录下最远距离是从哪个son转移过来的,fo[u];

第二遍dfs2求出当前节点从它fa出发能碰到给定点的最远距离up[u],那么对于当前点u,max(d1[u],up[u]),就是u到

所有给定点的最远的距离;

up[]的可以按照dfs的顺序递推求出,假设我们已经求出up[c1],现在求up[c2]

up[c2]表示从c2的fa结点也就是c1那边给定结点(红色圆点)到c2的最远距离

那么up[c2] 由两部分转移过来,一个是原先以c1为根的子树除去c2的分支后的最远距离,这就是为什么要记录最远是从哪个son转移过来

dd = (fo[c1] == c2 ? d2[c1] : d1[c1]);

还有就是up[c1];显然up[c2] = max(dd,up[c1])+1;(up[c1]表示从c6那边转移过来的值)

 #include<cstdio>
#include<cstring>
#include<cstdlib>
#include<iostream>
#include<algorithm>
#include<vector>
#include<cmath>
#define pbk push_back
using namespace std;
const int N = +;
const int INF = +;
vector<int> g[N];
int n,m,d;
bool p[N];
int d1[N],d2[N],fo[N],up[N];
int co[N];
void update(int c,int rt,int x,int &m1,int &m2) {
if (x >= m1) {
m2 = m1; m1 = x;
fo[rt] = c;
}else if (x < m1) {
if (x > m2) m2 = x;
}
}
void dfs1(int rt,int fa) {
int sz = g[rt].size();
d1[rt] = d2[rt] = -INF;
for (int i = ; i < sz; i++) {
int c = g[rt][i];
if (c == fa) continue;
dfs1(c,rt);
if (co[c]) {
update(c,rt,d1[c]+,d1[rt],d2[rt]);
co[rt] = ;
}
}
if (p[rt]) {
if (d1[rt] > && d2[rt] < ) d2[rt] = ;
if (d1[rt] < ) {
d1[rt] = ; fo[rt] = rt;
}
co[rt] = ;
}
}
void dfs2(int rt,int fa) {
int sz = g[rt].size();
for (int i = ; i < sz; i++) {
int c = g[rt][i];
if (c == fa) continue;
int dd = (fo[rt] == c ? d2[rt] : d1[rt]);
up[c] = max(dd, up[rt]) + ;
dfs2(c,rt);
}
}
void solve(){
memset(co,,sizeof(co));
memset(fo,-,sizeof(fo));
dfs1(,-);
up[] = -INF;
dfs2(,-); int ans = ;
for (int i = ; i <= n; i++) {
if (d1[i] <= d && up[i] <= d) ans++;
}
printf("%d\n",ans);
}
int main(){
while (~scanf("%d%d%d",&n,&m,&d)) {
memset(p,,sizeof(p));
for (int i = ; i < m; i++) {
int f; scanf("%d",&f);
p[f] = ;
}
for (int i = ; i <= n; i++) g[i].clear();
for (int i = ; i < n-; i++) {
int u,v; scanf("%d%d",&u,&v);
g[u].pbk(v); g[v].pbk(u);
}
solve();
}
return ;
}

hdu4679 http://acm.hdu.edu.cn/showproblem.php?pid=4679

题意:给n个结点的树,每条边上都有一个w,去掉一条边后变成两颗树,选一条边使得w*max(diaA,diaB);其中diaA,diaB表示树的直径(树上最长路);

分析:删掉一条边后,分成两部分,一部分是原先的子树,对于所有子树我们可以一遍dfs求出它的dia,问题我们该怎么求出

另外一部分的dia,设dia[u]表示从u的fa那一边能得到的最大直径,dis[u]表示u的fa那边能得到的最大距离,

那么这一部分的dia就是max(dia[u],dis[u]+1+md, dd);其中md表示删掉边后以u为根的子树叶子节点到u的最远距离,

dd表示删掉边后以u为根的子树的直径;这样就可以求出删掉一条边后的解了;

dd是B部分,dis[u]+1+md是A,B和起来的部分,dia[u]表示A部分;

接着就是该如何求dia[u], dis[u];

dis[u]的求法和上一题一样,dia[u]的求法也差不多

在第一遍dfs的时候保留下以u为根的子树到u的最大,次大,次次大的距离,并且保存下最大,次大是从哪个son转移过来的,

这样就可以再第二遍dfs的时候递推求出dia[u],dis[u];

 #include<cstdio>
#include<cstring>
#include<cstdlib>
#include<iostream>
#include<algorithm>
#include<cmath>
#include<vector>
#include<set>
#pragma comment(linker, "/STACK:1024000000,1024000000")
#define pbk push_back
using namespace std;
const int N = +;
const int INF = +;
struct edge{
int v,w,id;
edge(int v,int w,int id):v(v),w(w),id(id){}
};
vector<edge> g[N];
int n;
int md1[N],md2[N],md3[N],fro1[N],fro2[N];
void update(int x,int c,int rt) {
if (x >= md1[rt]) {
md3[rt] = md2[rt];
md2[rt] = md1[rt]; fro2[rt] = fro1[rt];
md1[rt] = x; fro1[rt] = c;
}else if (x >= md2[rt]){
md3[rt] = md2[rt];
md2[rt] = x; fro2[rt] = c;
}else if (x > md3[rt]) md3[rt] = x;
}
void dfs1(int rt,int fa) {
int sz = g[rt].size();
md1[rt] = md2[rt] = md3[rt] = ;
for (int i = ; i < sz; i++) {
edge c = g[rt][i];
if (c.v == fa) continue;
dfs1(c.v,rt);
update(md1[c.v]+,c.v,rt);
}
}
int dis[N],dia[N];
int an1,an2;
void dfs2(int rt,int fa) {
int sz = g[rt].size();
for (int i = ; i < sz; i++) {
edge c = g[rt][i];
if (c.v == fa) continue;
int dd ,md;
if (fro1[rt] == c.v) {
dd = md2[rt] + md3[rt]; md = md2[rt];
}else if (fro2[rt] == c.v) {
dd = md1[rt] + md3[rt]; md = md1[rt];
} else {
dd = md1[rt] + md2[rt]; md = md1[rt];
}
dd = max(dd, max(dia[rt],dis[rt] + + md));
dia[c.v] = dd;
dd = max(dd, md1[c.v] + md2[c.v]);
dis[c.v] = max(md, dis[rt] + );
if (an1 == -) {
an1 = c.id; an2 = dd * c.w;
}else if (dd * c.w < an2) {
an1 = c.id; an2 = dd * c.w;
}else if (dd * c.w == an2 && c.id < an1) {
an1 = c.id;
} dfs2(c.v,rt);
}
}
void solve(){
dfs1(,-);
dia[] = -INF; dis[] = -INF;
an1 = -;
dfs2(,-);
printf("%d\n",an1);
}
int main(){
int T,cas = ; scanf("%d",&T);
while (T--) {
scanf("%d",&n);
for (int i = ; i <= n; i++) g[i].clear();
for (int i = ; i < n; i++) {
int u,v,w; scanf("%d%d%d",&u,&v,&w);
g[u].pbk(edge(v,w,i));
g[v].pbk(edge(u,w,i));
}
printf("Case #%d: ",++cas);
solve();
}
return ;
}

hdu 4661 http://acm.hdu.edu.cn/showproblem.php?pid=4661

题意:n个,他们之间的关系是一棵树,每个人都有一个消息,每次可以选一个人把他知道的所有消息传给另一个人,问

所有人知道所有消息的方案数,假设要传k次,其中只要有一次传出的人或接受的人不同都算不同;

分析:每个人至少要把自己的消息传出去,至少要接受一次消息,很明显,确定树根后只要从叶子节点一层一层传上来

直到跟然后再从跟传下去,一共(n-1)*2,是最优的;求方案数,首先是从叶子节点传到根,其实就是所有节点的拓扑序的个数;

设f[u]表以u为根节点的子树的拓扑序的总数num[u]表示子树的结点数,

那么f[u] = f[son1] * f[son2] * f[soni] * (num[u]-1)!/(num[son1]! * num[son2]! * num[soni] ! );

通过一遍dfs在确定根后,可以求出所有子树的f[],在第二遍dfs的时候通过维护从当前结点的fa那边的拓扑序列,和num[]值

递推求出以当前结点为树根的最终方案;

然后是根传到叶子结点是等价于从叶子结点传到根的,所有累加所有方案数的平方就是最终解;

 #include<cstdio>
#include<cstring>
#include<cstdlib>
#include<iostream>
#include<algorithm>
#include<cmath>
#include<vector>
#pragma comment(linker, "/STACK:1024000000,1024000000")
#include<set>
using namespace std;
typedef long long LL;
const int N = +;
const int Mod = 1e9+;
vector<int> g[N];
int n;
LL ifac[N],fac[N];
void ex_gcd(LL a, LL b, LL &g, LL &x, LL &y) {
if (b == ) {
g = a; x = ; y = ;
}else {
ex_gcd(b, a%b, g, y, x);
y -= x * (a/b);
}
}
LL inv(LL a, LL n) {
LL x,y,g;
ex_gcd(a,n,g,x,y);
return g == ? (x % n + n) % n : -;
}
void init(){
fac[] = ; ifac[] = ;
for (int i = ; i < N; i++) {
fac[i] = fac[i-] * i % Mod;
ifac[i] = inv(fac[i],Mod);
}
}
int num[N],f[N];
void dfs1(int rt,int fa) {
int sz = g[rt].size();
num[rt] = ; f[rt] = ;
for (int i = ; i < sz; i++) {
int c = g[rt][i];
if (c == fa) continue;
dfs1(c,rt);
num[rt] += num[c];
f[rt] = (LL)f[rt] * f[c] % Mod * ifac[num[c]] % Mod;
}
num[rt] += ;
f[rt] = (LL)f[rt] * fac[num[rt] - ] % Mod;
}
LL ans;
int up[N],cn[N];
void dfs2(int rt,int fa) {
int sz = g[rt].size();
for (int i = ; i < sz; i++) {
int c = g[rt][i];
if (c == fa) continue; LL tmp = (LL)f[rt] * ifac[num[rt] - ] % Mod * fac[num[c]] % Mod * inv(f[c],Mod) % Mod ;
tmp = tmp * up[rt] % Mod * ifac[n - num[rt]] % Mod * fac[n - num[c] - ] % Mod;
up[c] = tmp; LL tp = f[c] * ifac[num[c] - ] % Mod * tmp % Mod * ifac[n - num[c]] % Mod * fac[n-] % Mod;
ans = (ans + tp * tp) % Mod;
dfs2(c,rt);
}
}
void solve(){
dfs1(,-);
ans = (LL)f[] * f[] % Mod;
up[] = ;
dfs2(,-);
printf("%I64d\n",ans);
}
int main(){
init();
int T; scanf("%d",&T);
while (T--) {
scanf("%d",&n);
for (int i = ; i <= n; i++) g[i].clear();
for (int i = ; i < n-; i++) {
int u,v; scanf("%d%d",&u,&v);
g[u].push_back(v); g[v].push_back(u);
}
solve();
}
return ;
}

hdu 4276 http://acm.hdu.edu.cn/showproblem.php?pid=4276

题意:给你一棵树,路径上有一个花费,每个节点有一个价值,问在T时间内,从1走到n能得到的最大价值;

分析:首先因为在树上,所有从1到n的路径是唯一的,也就是唯一路径上的边我们只会走一次,然后我们可以想象

如果把1和n结点拉直后,会变成一个森林并且树的根都是在路径上的,并且如果要去拿那里的价值每条路径都要经历2边;

思路就很明了,先预处理出所有路径上的点,然后以路径上的结点为子树根求出以每个根花费j能得到的最大代价,

最后就是分组背包,每组选一个;

 #include<cstdio>
#include<iostream>
#include<cstdlib>
#include<algorithm>
#include<cmath>
#include<cstring>
#include<vector>
#define mp make_pair
#define pbk push_back
using namespace std;
const int N = +;
typedef pair<int,int> pii;
int dp[N][+];
int n,T;
vector<pii> G[N];
int val[N];
int On[N], f, sti;
void dfs_find(int u,int fa,int dep) {
int sz = G[u].size();
On[u] = ;
if (u == n) {
f = ;
return;
}
for (int i = ; i < sz; i++) {
int v = G[u][i].first;
if (v == fa) continue;
sti = sti + G[u][i].second;
dfs_find(v,u,dep+);
if (f) return;
sti = sti - G[u][i].second;
}
On[u] = ;
}
void dfs(int u,int fa) {
int sz = G[u].size();
for (int i = ; i <= T - sti; i++) dp[u][i] = val[u];
for (int i = ; i < sz; i++) {
int v = G[u][i].first;
if (On[v] || v == fa) continue;
dfs(v, u);
int ti = G[u][i].second * ;
for (int j = T - sti; j >= ; j--) {
for (int k = ; k <= j - ti; k++) {
dp[u][j] = max(dp[u][j], dp[u][j - k - ti] + dp[v][k]);
}
}
} }
int an[+];
void solve(){
memset(On,,sizeof(On));
f = ; sti = ;
dfs_find(,-,);
if (sti > T) {
puts("Human beings die in pursuit of wealth, and birds die in pursuit of food!");
return;
}
memset(dp,,sizeof(dp));
for (int i = ; i <= n; i++) {
if (On[i]) {
dfs(i,-);
}
}
//分组背包
memset(an,,sizeof(an));
for (int i = ; i <= n; i++) if (On[i]){
for (int j = T - sti; j >= ; j--) {
for (int k = ; k <= j; k++) {
an[j] = max(an[j], an[j - k] + dp[i][k]);
}
}
}
int idx = ;
for (int i = ; i <= T - sti; i++) {
if (an[i] > an[idx]) idx = i;
}
printf("%d\n",an[idx]); }
int main(){
while (~scanf("%d%d",&n,&T)) {
for (int i = ; i <= n; i++) G[i].clear();
for (int i = ; i < n-; i++) {
int u,v,w; scanf("%d%d%d",&u,&v,&w);
G[u].pbk(mp(v,w)); G[v].pbk(mp(u,w));
}
for (int i = ; i <= n; i++) scanf("%d",val+i);
solve();
}
return ;
}

hdu 4044

题意:可以在结点上建至多一个炮台,花费已知,攻击力已知,求只有m费用的情况下从结点1到叶子结点的路径上攻击力之和的最小值最大;

分析:对于结点1,首先我们要分配m使得它的各个son的攻击力之和最小值经历大,dp[u][m]表示的就是以结点u为子树根花费m能得到的对于该子树的最小值最大;

现在的问题就是这么推出dp[u][m]; 显然 对已son1来说dp[u][m] = dp[son1][m]; 对于son2: tp = max(tp,min(dp[u][m-k], dp[son2][k])); dp[u][m] = tp;此时的dp[u][m-k]表示的前i个son都取完之后最小的攻击力最大的值;  当遍历完son之后,那么再加上u位置能建立的炮台,就是最终的答案;

trick : 因为花费有为zero的,所有直接dp[u][m] = max(dp[u][m], dp[u][m-x] + y);是有问题的,当存在两次以上花费为0的数据就会错误,所以要预先处理处理花费为i最大攻击力;

 #include<cstdio>
#include<cstring>
#include<iostream>
#include<cmath>
#include<algorithm>
#include<cstdlib>
#include<vector>
#include<map>
#define pbk push_back
#define mp make_pair
using namespace std;
const int N = +;
typedef pair<int,int> pii;
int dp[N][+];
int n,m;
vector<int> g[N];
vector<pii> tow[N];
int tp[N][+];
void dfs(int u,int fa) {
bool first = true;
for (int i = ; i < g[u].size(); i++) {
int v = g[u][i];
if (v == fa) continue;
dfs(v,u);
if (first) {
for (int j = ; j <= m; j++) dp[u][j] = dp[v][j];
first = false;
continue;
}
for (int j = m; j >= ; j--) {
int tp = ;
for (int k = ; k <= j; k++) {
int tmp = min (dp[u][j-k], dp[v][k]);
//if (first) tmp = dp[v][k];
tp = max(tp, tmp);
}
dp[u][j] = tp;
}
//first = false;
} for (int i = m; i >= ; i--) {
/* for (int j = 0; j < tow[u].size(); j++) {
int x = tow[u][j].first, y = tow[u][j].second;
dp[u][i] = max(dp[u][i], dp[u][i-x] + y);
}*/
for (int j = ; j <= i; j++) dp[u][i] = max(dp[u][i], dp[u][i - j] + tp[u][j]);
}
}
void solve() {
for (int i = ; i <= n; i++) {
for (int j = ; j <= m; j++) tp[i][j] = max(tp[i][j], tp[i][j-]);
}
memset(dp, , sizeof(dp));
dfs(,-);
int idx = ;
for (int i = ; i <= m; i++) {
if (dp[][i] > dp[][idx]) idx = i;
}
printf("%d\n",dp[][idx]);
}
int main(){
int T; scanf("%d",&T);
while (T--) {
scanf("%d",&n);
for (int i = ; i <= n; i++) g[i].clear(); for (int i = ; i < n-; i++) {
int u,v; scanf("%d%d",&u,&v);
g[u].pbk(v); g[v].pbk(u);
}
scanf("%d",&m);
memset(tp,,sizeof(tp));
for (int i = ; i <= n; i++) {
int c; scanf("%d",&c);
tow[i].clear();
for (int j = ; j < c; j++) {
int u,v; scanf("%d%d",&u,&v);
// tow[i].pbk(mp(u,v));
tp[i][u] = max(tp[i][u],v);
}
} solve(); } return ;
} /*
2
2
1 2
30
3 10 20 20 40 30 50
3 10 30 20 40 30 45
4
2 1
3 1
1 4
9
3 10 20 20 40 30 50
3 10 30 20 40 30 45
3 10 30 20 40 30 35
3 10 30 20 40 30 35 */

CF 219D

题意:一棵树,路径是由方向的,选一个节点,使得旋转最少的边使得从该结点到所有结点都有路;

分析:如果路径分别标记为1,-1那么就是统计以某个结点为根,-1的最小个数,dp[u]表示以u为根的子树下有多少个-1;

 #include<cstdio>
#include<cstring>
#include<cstdlib>
#include<iostream>
#include<cmath>
#include<algorithm>
#include<vector>
#include<set>
#define pbk push_back
#define mp make_pair
using namespace std;
const int N = + ;
typedef pair<int,int> pii;
vector<pii> g[N];
int n;
int dp[N];
vector<int> an;
int up;
void dfs1(int u,int fa) {
int sz = g[u].size();
dp[u] = ;
for (int i = ; i < sz; i++) {
int v = g[u][i].first;
if (v == fa) continue;
dfs1(v,u);
dp[u] += dp[v] + (g[u][i].second == - ? : );
}
}
void update(int u,int v) {
if (u < up) {
up = u;
an.clear(); an.pbk(v);
}else if ( u == up ) an.pbk(v);
}
void dfs2(int u,int fa,int ba) {
int sz = g[u].size(); for (int i = ; i < sz; i++) {
int v = g[u][i].first;
if (v == fa) continue;
int tp = dp[u] - dp[v] - (g[u][i].second == - ? : ) + ba + (g[u][i].second == ? : );
update(dp[v] + tp,v);
dfs2(v,u,tp);
}
}
void solve(){
memset(dp,,sizeof(dp));
dfs1(,-);
up = dp[];
an.clear();
an.pbk();
dfs2(,-,);
printf("%d\n",up);
sort(an.begin(),an.end());
for (int i = ; i < an.size(); i++) {
printf("%d%c",an[i],i == an.size() - ? '\n' : ' ');
}
}
int main() {
while (~scanf("%d",&n)) {
for (int i = ; i <= n; i++) g[i].clear();
for (int i = ; i < n-; i++) {
int u,v; scanf("%d%d",&u,&v);
g[u].pbk(mp(v,)); g[v].pbk(mp(u,-));
}
solve();
}
return ;
}

CF 120F http://codeforces.com/problemset/problem/120/F

题意:给你n棵树,累加每棵树的直径;

分析:直接求;该题CF上是文件读入的;

 #include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#include<cstdlib>
#include<cmath>
#include<vector>
#define pbk push_back
#define mp make_pair
using namespace std;
typedef pair<int,int> pii;
const int N = +;
vector<int> g[N];
int n;
int an;
int dia[N],di1[N],di2[N],fo[N],mxdia;
void update(int x,int u,int v) {
if (x > di1[u]) {
di2[u] = di1[u];
di1[u] = x;
fo[u] = v;
}else if (x == di1[u] && x > di2[u]) di2[u] = x;
}
void dfs1(int u,int fa) {
int sz = g[u].size();
di1[u] = di2[u] = ;
for (int i = ; i < sz; i++) {
int v = g[u][i];
if (v == fa) continue;
dfs1(v,u);
update(di1[v] + ,u,v);
}
dia[u] = di1[u] + di2[u];
}
void dfs2(int u,int fa,int up) {
int sz = g[u].size();
for (int i = ; i < sz; i++) {
int v = g[u][i];
if (v == fa) continue;
int tp = max((fo[u] == v ? di2[u] : di1[u]) , up ) + ;
mxdia = max(mxdia, di1[v] + tp);
dfs2(v,u,tp);
}
}
void solve(){
dfs1(,-);
mxdia = dia[];
dfs2(,-,);
an += mxdia;
}
int main(){
int c;
freopen("input.txt","r",stdin);
freopen("output.txt","w",stdout);
while (~scanf("%d",&c)) {
an = ;
while (c--) {
scanf("%d",&n);
for (int i = ; i <= n; i++) g[i].clear();
for (int i = ; i < n-; i++) {
int u,v; scanf("%d%d",&u,&v);
g[u].pbk(v); g[v].pbk(u);
}
solve();
}
printf("%d\n",an);
}
return ;
}

CF 212E

题意:一棵树上放两种餐厅,不同的餐厅不能相邻,每种餐厅至少一个,问最多能放几个,并且输出所有可行方案;

分析:显然对于一棵上来说,肯定可以选出一个节点,它有至少两个分支,所以最多的放置数为n-1

对于方案数,我们可以枚举结点,然后对于至少有两个分支的结点进行背包,求出第一中餐厅能放的值,那么所有答案并就是方案;

 #include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#include<cmath>
#include<cstdlib>
#include<vector>
#define pbk push_back
using namespace std;
const int N = +;
vector<int> g[N];
int n;
int an[N];
int dp[N]; void dfs1(int u,int fa) {
int sz = g[u].size();
dp[u] = ;
for (int i = ; i < sz; i++) {
int v = g[u][i];
if (v == fa) continue;
dfs1(v,u);
dp[u] += dp[v];
}
}
void dfs2(int u,int fa,int up) {
int sz = g[u].size();
bool tmp[N];
memset(tmp,,sizeof(tmp));
tmp[] = ;
int cnt = ;
for (int i = ; i < sz; i++) {
int v = g[u][i];
if (v == fa) continue;
cnt++;
dfs2(v,u,up + dp[u] - dp[v]);
for (int j = n; j >= dp[v]; j--) {
if (tmp[j - dp[v]] == ) tmp[j] = ;
}
}
for (int j = n; j >= up; j--) {
if (tmp[j - up]) tmp[j] = ;
}
if (up == && cnt == ) return;
for (int j = n; j >= ; j--) {
if (tmp[j]) an[j] = ;
}
}
void solve() {
memset(an,,sizeof(an));
dfs1(,-); dfs2(,-,);
int cnt = ;
for (int i = ; i < n-; i++) if (an[i]) cnt++;
printf("%d\n",cnt);
for (int i = ; i < n-; i++) {
if (an[i]) printf("%d %d\n",i,n--i);
}
}
int main(){
while (~scanf("%d",&n)) {
for (int i = ; i <= n; i++) g[i].clear();
for (int i = ; i < n-; i++) {
int u,v; scanf("%d%d",&u,&v);
g[u].pbk(v); g[v].pbk(u);
}
solve();
}
return ;
}

树上的DP的更多相关文章

  1. LOJ #2542. 「PKUWC 2018」随机游走(最值反演 + 树上期望dp + FMT)

    写在这道题前面 : 网上的一些题解都不讲那个系数是怎么推得真的不良心 TAT (不是每个人都有那么厉害啊 , 我好菜啊) 而且 LOJ 过的代码千篇一律 ... 那个系数根本看不出来是什么啊 TAT ...

  2. HDU 5956 The Elder (树上斜率DP)

    题意:给定上一棵树,然后每条边有一个权值,然后每个点到 1 的距离有两种,第一种是直接回到1,花费是 dist(1, i)^2,还有另一种是先到另一个点 j,然后两从 j 向1走,当然 j 也可以再向 ...

  3. loj 2542 随机游走 —— 最值反演+树上期望DP+fmt

    题目:https://loj.ac/problem/2542 因为走到所有点的期望就是所有点期望的最大值,所以先最值反演一下,问题变成从根走到一个点集任意一点就停止的期望值: 设 \( f[x] \) ...

  4. 『保卫王国 树上倍增dp』

    保卫王国 Description Z 国有n座城市,n - 1条双向道路,每条双向道路连接两座城市,且任意两座城市 都能通过若干条道路相互到达. Z 国的国防部长小 Z 要在城市中驻扎军队.驻扎军队需 ...

  5. [CSP-S模拟测试]:点亮(状压DP+树上背包DP)

    题目传送门(内部题121) 输入格式 第一行,一个正整数$n$. 第二行,$n-1$个正整数$p_2,p_3,...,p_n$.保证$p_u$是在$1$到$u-1$中等概率随机选取的. 接下来$n$行 ...

  6. 软件安装:树上分组DP/tarjan缩点/(也许基环树?)

    提炼:tarjan环缩成点,建0虚根,跑树形DP,最难的是看出可能有n个点n条边然后缩点,n个点n条边可能不只有一个环 n个点n条边->基环树: 基环树,也是环套树,简单地讲就是树上在加一条边. ...

  7. 树形DP——动态规划与数据结构的结合,在树上做DP

    本文始发于个人公众号:TechFlow,原创不易,求个关注 今天是算法与数据结构的第15篇,也是动态规划系列的第4篇. 之前的几篇文章当中一直在聊背包问题,不知道大家有没有觉得有些腻味了.虽然经典的文 ...

  8. 动态规划入门——动态规划与数据结构的结合,在树上做DP

    本文由TechFlow原创,本博文仅作为知识点学习,不会用于任何商业用途! 今天我们来看一个有趣的问题,通过这个有趣的问题,我们来了解一下在树形结构当中做动态规划的方法. 这个问题题意很简单,给定一棵 ...

  9. 【算法学习笔记】动态规划与数据结构的结合,在树上做DP

    前置芝士:Here 本文是基于 OI wiki 上的文章加以修改完成,感谢社区的转载支持和其他方面的支持 树形 DP,即在树上进行的 DP.由于树固有的递归性质,树形 DP 一般都是递归进行的. 基础 ...

随机推荐

  1. SVN 显示灰色减号代表什么意思

    灰色减号(ignored):意思就是忽略的意思,不对其进行版本控制,忽略对其进行的任何操作

  2. iOS9 class dump header

    获取系统私有API,网上有很多资料总结了一下就三种方式: 使用class-dump可以提取系统私有API列表 使用class-dump+DumpFrameworks.pl,这个可以一次性提取所有系统F ...

  3. HDU 5353 Average 糖果分配(模拟,图)

    题意:有n个人坐在圆桌上,每个人带着糖果若干,每次只能给旁边的人1科糖果,而且坐相邻的两个人最多只能给一次(要么你给我,要么我给你),问是否能将糖果平均分了. 思路: 明显每个人最多只能多于平均值2个 ...

  4. php 在函数内引用全局变量 讲解引用

    <?php $var1 = "Example variable"; $var2 = ""; function global_references($use ...

  5. 决定undo表空间的大小

    1.查询每秒最高需要的undo的数据块[每个块8k大小] sys)) from v$undostat; )) --------------------------------------------- ...

  6. MySQL5.6 ALTER TABLE 分析和测试

    在MySQL5.5和之前版本,在运行的生产环境对大表(超过数百万纪录)执行Alter操作是一件很困难的事情.因为将重建表和锁表,影响用户者的使用.因此知道Alter操作何时结束对我们是非常重要的.甚至 ...

  7. 【转】Linux设备驱动之mmap设备操作

    原文网址:http://www.cnblogs.com/geneil/archive/2011/12/08/2281222.html 1.mmap系统调用 void *mmap(void *addr, ...

  8. Mysql 不同版本 说明

    Mysql 的官网下载地址: http://dev.mysql.com/downloads/ 在这个下载界面会有几个版本的选择. 1. MySQL Community Server 社区版本,免费,但 ...

  9. 部署K2 Blackpearl流程时出错(由于目标计算机积极拒绝,无法连接)

    转:http://www.cnblogs.com/dannyli/archive/2011/12/01/2270118.html 亲,如果你也遇到过这个问题,就请继续往下看哦 在部署K2 Blackp ...

  10. linux 开机自动运行

    1.开机启动时自动运行程序 Linux加载后, 它将初始化硬件和设备驱动, 然后运行第一个进程init.init根据配置文件继续引导过程,启动其它进程.通常情况下,修改放置在 /etc/rc或 /et ...