BZOJ3257 : 树的难题

设$f[x][i][j]$表示以$x$为根的子树,与$x$连通部分有$i$个黑点,$j$个白点,不联通部分都是均衡的最小代价。若$i>1$,则视作$1$;若$j>2$,则视作$2$。

然后进行树形DP即可,转移的时候如果不要那棵子树,那么那棵子树的状态必须满足$!i||j<2$。

时间复杂度$O(n)$。

#include<cstdio>
#define rep(i,n) for(int i=0;i<n;i++)
typedef long long ll;
const int N=300010;
const ll inf=1LL<<60;
int T,n,i,x,y,z,a[N],g[N],v[N<<1],w[N<<1],nxt[N<<1],ed;
ll f[N][2][3],h[2][3],ans;
inline void read(int&a){char c;while(!(((c=getchar())>=‘0‘)&&(c<=‘9‘)));a=c-‘0‘;while(((c=getchar())>=‘0‘)&&(c<=‘9‘))(a*=10)+=c-‘0‘;}
inline void up(ll&a,ll b){if(a>b)a=b;}
inline void add(int x,int y,int z){v[++ed]=y;w[ed]=z;nxt[ed]=g[x];g[x]=ed;}
inline int fix(int x){return x<2?x:2;}
void dfs(int x,int y){
  rep(A,2)rep(B,3)f[x][A][B]=inf;
  f[x][0][0]=0;
  for(int i=g[x];i;i=nxt[i])if(v[i]!=y){
    int u=v[i];
    dfs(u,x);
    rep(A,2)rep(B,3)h[A][B]=inf;
    rep(A,2)rep(B,3)if(f[x][A][B]<inf)rep(C,2)rep(D,3)if(f[u][C][D]<inf){
      up(h[A|C][fix(B+D)],f[x][A][B]+f[u][C][D]);
      if(!C||D<2)up(h[A][B],f[x][A][B]+f[u][C][D]+w[i]);
    }
    rep(A,2)rep(B,3)f[x][A][B]=h[A][B];
  }
  rep(A,2)rep(B,3)h[A][B]=inf;
  rep(A,2)rep(B,3)if(f[x][A][B]<inf)up(h[A|!a[x]][fix(B+(a[x]==1))],f[x][A][B]);
  rep(A,2)rep(B,3)f[x][A][B]=h[A][B];
}
int main(){
  for(read(T);T--;printf("%lld\n",ans)){
    read(n);
    for(ed=0,i=1;i<=n;i++)read(a[i]),g[i]=0;
    for(i=1;i<n;i++)read(x),read(y),read(z),add(x,y,z),add(y,x,z);
    dfs(1,0);
    ans=inf;
    rep(A,2)rep(B,3)if(!A||B<2)up(ans,h[A][B]);
  }
  return 0;
}

  

时间: 2024-08-13 11:40:01

BZOJ3257 : 树的难题的相关文章

【XSY2307】树的难题

Description Solution 看到这种路径统计问题,一般就想到要用点分治去做. 对于每个重心\(u\),统计经过\(u\)的合法的路径之中的最大值. 第一类路径是从\(u\)出发的,直接逐个子树深搜统计就可以了.第二类路径是由两棵不同子树中的两条第一类路径拼接而成的. 如果仅仅是统计长度在\([l,r]\)之间的路径有多少条,经典的统计+容斥做法就可以解决.然而现在的问题比较复杂,一来不好容斥,二来两两路径配对需要有判定条件:两条路径的接口边颜色是否相同. 我们可以采用一种不需要容斥

[BJOI2017]树的难题

Description 给你一棵 n 个点的无根树.树上的每条边具有颜色. 一共有 m 种颜色,编号为 1 到 m.第 i 种颜色的权值为 ci.对于一条树上的简单路径,路径上经过的所有边按顺序组成一个颜色序列,序列可以划分成若干个相同颜色段. 定义路径权值为颜色序列上每个同颜色段的颜色权值之和.请你计算,经过边数在 l 到 r 之间的所有简单路径中, 路径权值的最大值. Input 第一行, 四个整数 n, m, l, r. 第二行, n 个整数 c1, c2, --, cm,由空格隔开.依次

【JZOJ3347】树的难题

description analysis 比较麻烦树形\(DP\) 不过这个我还是不算很懂-- 下次要注意思考,不要怕麻烦 code #pragma GCC optimize("O3") #pragma G++ optimize("O3") #include<stdio.h> #include<string.h> #include<algorithm> #include<queue> #define MAXN 3000

数据结构之二分查找树总结

说明:本文仅供学习交流,转载请标明出处,欢迎转载! 二分查找树BST(也叫二叉查找树.二叉排序树)的提出是为了提供查找效率,之所以称为二分查找树,因为该二叉树对应着二分查找算法,查找平均的时间复杂度为o(logn),所以该数据结构的提出是为了提高查找效率. 定义 二分查找树或者是一棵空树,或者具有下列性质: 1.若它的左子树不为空,则左子树上所有结点的值均小于根结点的值: 2.若它的右子树不为空,则右子树上所有结点的值均大于根结点的值: 3.它的左右子树均为二分查找树. 操作 二分查找树的操作主

HDU 3911 Black And White 分段树 题解

Problem Description There are a bunch of stones on the beach; Stone color is white or black. Little Sheep has a magic brush, she can change the color of a continuous stone, black to white, white to black. Little Sheep like black very much, so she wan

线段树分裂合并

线段树分裂合并 我先接触的是线段树合并所以先讲线段树合并. 首先,用来合并的线段树必须是动态开点的.线段树合并所做的事就是合并两棵动态开点线段树的信息,对于两棵动态开点线段树,可能会存在一些公共节点,我们所要做的就是合并这些节点的信息,然后把其他节点的信息继承.理清思路之后,剩下的事就是.设初始信息的个数是\(n\),值域是\(m\),对于每一个初始信息一次这样的操作是\(\log m\)的,而每个信息只会被合并一次,所以一般的线段树合并是\(O(n \log m)\)的.非常好理解,直接上模板

从头开始的点分治

今天发现之前有篇随笔忘发布了-- TREE [题意]给你一棵树,以及这棵树上边的距离.问有多少对点它们两者间的距离小于等于K. [分析]考虑树根rt,可以处理出rt到其子树中的节点的距离,设为dis[].利用dis数组的值,我们可以轻易地得到所有"端点在rt的子树中,长度≤k的,且经过rt的路径"数量(具体做法是将dis的值排序,然后双指针扫描).要去除其中的非简单路径数量,只需要对rt的每个"儿子的子树"做一遍类似的过程(区别在于儿子的子树中的路径长度需要≤k-l

hdu 1251 统计难题(字典树)

Problem Description Ignatius最近遇到一个难题,老师交给他很多单词(只有小写字母组成,不会有重复的单词出现),现在老师要他统计出以某个字符串为前缀的单词数量(单词本身也是自己的前缀). Input 输入数据的第一部分是一张单词表,每行一个单词,单词的长度不超过10,它们代表的是老师交给Ignatius统计的单词,一个空行代表单词表的结束.第二部分是一连串的提问,每行一个提问,每个提问都是一个字符串. 注意:本题只有一组测试数据,处理到文件结束. Output 对于每个提

HDU 1251 统计难题 (字典树)(查询是否为前缀)

统计难题 Time Limit: 4000/2000 MS (Java/Others)    Memory Limit: 131070/65535 K (Java/Others)Total Submission(s): 37626    Accepted Submission(s): 13858 Problem Description Ignatius最近遇到一个难题,老师交给他很多单词(只有小写字母组成,不会有重复的单词出现),现在老师要他统计出以某个字符串为前缀的单词数量(单词本身也是自己的