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. Spring安全框架 Spring Security

    Spring Security 的前身是 Acegi Security ,是 Spring 项目组中用来提供安全认证服务的框架. Spring Security  为基于J2EE企业应用软件提供了全面 ...

  2. Javascript零散知识点总结

    一.Javascript获取系统当前时间 <script type="text/javascript"> var time = new Date(); var date ...

  3. UVa 10763 (STL) Foreign Exchange

    看到大神说location的值不会超过1000,所以这就简单很多了,用一个deg数组记录下来每个点的度,出度-1,入读+1这样. 最后判断每个点的度是否为0即可. 至于为什么会这样,据说是套数据套出来 ...

  4. POJ 2524 (简单并查集) Ubiquitous Religions

    题意:有编号为1到n的学生,然后有m组调查,每组调查中有a和b,表示该两个学生有同样的宗教信仰,问最多有多少种不同的宗教信仰 简单并查集 //#define LOCAL #include <io ...

  5. HDU 2064 (递推) 汉诺塔III

    将柱子从左到右依次编号为A.B.C 设将n个盘子从一端移动到另一端的最少步数为f(n) 则f(n)和f(n-1)的递推关系为:f(n) = 3 × f(n-1) + 2 初始状态A柱子上面有n个盘子, ...

  6. Myeclipse 安装Aptana3.2 插件

    转自(http://www.cnblogs.com/yinger/archive/2011/08/29/2157193.html) 安装步骤: 1.下载aptana3.2 Eclipse Plugin ...

  7. exception is org.hibernate.exception.DataException: Could not execute JDBC batch update at

    没有什么问题,但是却报了Could not execute JDBC batch update的错,主要是配置文件设置了关联,数据却没有关联造成的,只要数据正确就没有问题. 另外,造成这个原因的还可能 ...

  8. Hibernate-Criteria Queries

    1.实例 接口org.hibernate.Criteria针对特殊持久层类进行查询,Sesion是Criteria的工厂: Criteria crit = sess.createCriteria(Ca ...

  9. ADG打补丁

    1 产品DG备库安装 16494615 补丁 主库停止向备库传输日志 alter system set log_archive_dest_state_2=defer; alter system set ...

  10. 当sql报错代码,不允许对表操作的原因

    不允许对表 表名 执行操作,原因码为 原因码. 说明 限制对表 表名 的访问.原因基于下列原因码 原因码: 1 该表处于“设置完整性暂挂无访问”状态.未强制表的完整性并且表的内容可能是无效的.如果从属 ...