树形dp 结论题 bzoj5024 [Jsoi2010]游戏

http://www.lydsy.com/JudgeOnline/problem.php?id=5024

首先吐槽一下题面是有错误的
那一个"或" 应该改成","



这道题目条件是非常绕的
先看一个简化的问题
我们对于\(u\), \(v\)
如果\(u-v\)是良的 那么染成白色
否则染成黑色
问题转化成一个经典问题

给定一个完全图 求同色三角形的个数

做法就是考虑非同色三角形的个数
找角的个数\(jiao\) 答案就是\(C_n ^ 3 - jiao / 2\)



考虑如何在\(O(distance(u, v))\) 判断是否\(u - v\)是否是良的
观察计算\(P\)的方式
由于是末尾是\(011\)
可以考虑在模\(8\)的意义下来计算
于是$P = 1 \times s_1 + 5 \times s_2 + 1 \times s_3 ... $
此时考虑\(1 = 001_2\), \(5 = 101_2\)
末尾的\(01\)是相同的
于是我们只需要考虑倒数第三位是否是\(1\)或者是\(0\)
倒数第三位的贡献是由谁提供的呢?
分成两部分

  1. 所有数的和 的倒数第三位
  2. 偶数位的数 的倒数第一位

由于第一部分贡献已知
只需要看第二部分是否满足
问题变成

考虑一个\(01\)串 可以从头尾取
是否能满足偶数位为\(0\)为\(1\)

如果全\(0\)全\(1\)那么答案固定
否则\(01\)都是满足的
证明的话考虑两头类似于括号序列的消去

到现在为止
我们可以在\(O(distance(u, v))\) 判断是否\(u - v\)是否是良的



于是就可以树形dp了啊
记录状态\(f[sum][dep][k]\) 表示子树的答案
\(sum\)表示路径的和 模 \(8\)
\(dep\)表示最低位的和 模 \(4\)
\(k\) 表示 \(all_0 / all_1 / others\)
然后我们就求出了一个点为根的答案
然后换根 dp 一下就好了
复杂度\(O(n)\)



以下代码bzoj不能过 爆栈了.. xjoi可以过..
不会写手工栈.jpg

#pragma GCC optimize(2)
#pragma comment(linker, "/STACK:2048000000,2048000000")
#include<bits/stdc++.h>
#define int long long
#define fo(i, n) for(int i = 1; i <= (n); i ++)
#define out(x) cerr << #x << " = " << x << "\n"
#define type(x) __typeof((x).begin())
#define foreach(it, x) for(type(x) it = (x).begin(); it != (x).end(); ++ it)
using namespace std;
// by piano
template<typename tp> inline void read(tp &x) {
  x = 0; char c = getchar(); bool f = 0;
  for(; c < '0' || c > '9'; f |= (c == '-'), c = getchar());
  for(; c >= '0' && c <= '9'; x = (x << 3) + (x << 1) + c - '0', c = getchar());
  if(f) x = -x;
}
template<typename tp> inline void arr(tp *a, int n) {
  for(int i = 1; i <= n; i ++)
    cout << a[i] << " ";
  puts("");
}
const int N = 3e5 + 233;
struct E {
  int nxt, to;
}e[N << 1];
int head[N], e_cnt = 0;

inline void add(int u, int v) {
  e[++ e_cnt] = (E) {head[u], v}; head[u] = e_cnt;
}

struct Node {
  // sum % 8, dep % 4, all_0 / all_1 / others
  int f[8][4][3];

  inline void clear(void) {
    memset(f, 0, sizeof f);
  }

  inline void init(int val, int fff) {
    int sum = val % 8, dep = val & 1;
    int k = dep;
    f[sum][dep][k] += fff;
  }
  inline void ovo(void) {
    for(int sum = 0; sum < 8; sum ++)
      for(int dep = 0; dep < 4; dep ++)
        for(int k = 0; k < 3; k ++)
          if(f[sum][dep][k])
            printf("f[%lld][%lld][%lld] = %lld\n", sum, dep, k, f[sum][dep][k]);
  }
}p[N], tmp;
int n, fat, val[N], ans[N];

inline void U(Node &a, Node b, int fff) {
  for(int sum = 0; sum < 8; sum ++)
    for(int dep = 0; dep < 4; dep ++)
      for(int k = 0; k < 3; k ++)
        a.f[sum][dep][k] += b.f[sum][dep][k] * fff;
}

inline void Get(Node u, int val) {
  tmp.clear();
  for(int sum = 0; sum < 8; sum ++) {
    for(int dep = 0; dep < 4; dep ++) {
      int ns = (val % 8 + sum) % 8;
      int nd = ((val & 1) + dep) % 4;
      int ok = val & 1;
      if(ns >= 8 || nd >= 4) while(1);
      for(int k = 0; k < 3; k ++) {
        if(ok == k)
          tmp.f[ns][nd][k] += u.f[sum][dep][k];
        else
          tmp.f[ns][nd][2] += u.f[sum][dep][k];
      }
    }
  }
}

inline int Getans(const Node &u) {
  int ans = 0;
  for(int dep = 0; dep < 4; dep ++)
    ans += u.f[3][dep][0] + u.f[3][dep][2];
  for(int dep = 0; dep < 4; dep ++)
    if((dep / 2) % 2 == 0)
      ans += u.f[3][dep][1];
  for(int dep = 0; dep < 4; dep ++) {
    if((dep / 2) % 2 == 1)
      ans += u.f[7][dep][1];
    ans += u.f[7][dep][2];
  }
  return ans * (n - 1 - ans);
}

inline void dfs(int u, int fat) {
  for(int i = head[u]; i; i = e[i].nxt) {
    int v = e[i].to;
    if(v != fat) dfs(v, u), Get(p[v], val[u]), U(p[u], tmp, 1);
  }
  p[u].init(val[u], 1);
}

inline void frt(int u, int fat) {
  p[u].init(val[u], -1);
  ans[u] = Getans(p[u]);
  p[u].init(val[u], 1);
  for(int i = head[u]; i; i = e[i].nxt) {
    int v = e[i].to;
    if(v != fat) {
      Get(p[v], val[u]); U(p[u], tmp, -1);
      Get(p[u], val[v]); U(p[v], tmp, 1);
      frt(v, u);
      Get(p[u], val[v]); U(p[v], tmp, -1);
      Get(p[v], val[u]); U(p[u], tmp, 1);
    }
  }
}

main(void) {
  read(n);
  for(int i = 1; i <= n; i ++) {
    read(fat); read(val[i]); val[i] &= 7;
    add(i, fat); add(fat, i);
  }
  dfs(1, 0); frt(1, 0);
  int res = 0;
  fo(i, n) res += ans[i];
  res /= 2;
  cout << n * (n - 1) * (n - 2) / 6 - res << "\n";
}

原文地址:https://www.cnblogs.com/foreverpiano/p/8496438.html

时间: 2024-10-04 13:26:30

树形dp 结论题 bzoj5024 [Jsoi2010]游戏的相关文章

[bzoj1369][Baltic2003]Gem_树形dp_结论题

Gem bzoj-1369 Baltic-2003 题目大意:给你一棵树,让你往节点上添自然数,使得任意相邻节点的数不同且使得权值最小. 注释:n为结点个数,$1\le n\le 10^3$. 想法:呵呵,学长一直在骂结论题,我一直觉得没啥.. ... 知道tm碰见这玩意儿,卧槽?! 树上相邻节点不同色,颜色个数最小值至多为logn. 最后,附上丑陋的代码... ... #include <iostream> #include <cstring> #include <cstd

『战略游戏 最大利润 树形DP』

通过两道简单的例题,我们来重新认识树形DP. 战略游戏(luoguP1026) Description Bob喜欢玩电脑游戏,特别是战略游戏.但是他经常无法找到快速玩过游戏的办法.现在他有个问题.他要建立一个古城堡,城堡中的路形成一棵树.他要在这棵树的结点上放置最少数目的士兵,使得这些士兵能了望到所有的路.注意,某个士兵在一个结点上时,与该结点相连的所有边将都可以被了望到. 请你编一程序,给定一树,帮Bob计算出他需要放置最少的士兵. Input Format 输入文件中数据表示一棵树,描述如下

P2016 战略游戏——树形DP大水题

P2016 战略游戏 树形DP 入门题吧(现在怎么是蓝色标签搞不懂): 注意是看见每一条边而不是每一个点(因为这里错了好几次): #include<cstdio> #include<cstring> #include<algorithm> using namespace std; const int maxn=3010; int pre[maxn],last[maxn],other[maxn],l; void add(int x,int y) { l++; pre[l]

4.9 省选模拟赛 圆圈游戏 树形dp set优化建图

由于圆不存在相交的关系 所以包容关系形成了树的形态 其实是一个森林 不过加一个0点 就变成了树. 考虑对于每个圆都求出最近的包容它的点 即他的父亲.然后树形dp即可.暴力建图n^2. const int MAXN=100010; int n,m,len; struct wy { ll x,y,r,w; inline int friend operator <(wy a,wy b){return a.r<b.r;} }t[MAXN]; int f[MAXN]; int lin[MAXN],ver

树形 DP 总结

本文转自:http://blog.csdn.net/angon823/article/details/52334548 介绍 1.什么是树型动态规划 顾名思义,树型动态规划就是在"树"的数据结构上的动态规划,平时作的动态规划都是线性的或者是建立在图上的,线性的动态规划有二种方向既向前和向后,相应的线性的动态规划有二种方法既顺推与逆推,而树型动态规划是建立在树上的,所以也相应的有二个方向: 1.叶->根:在回溯的时候从叶子节点往上更新信息 2.根 - >叶:往往是在从叶往根d

选课 树形DP+多叉树转二叉树+dfs求解答案

问题 A: 选课 时间限制: 1 Sec  内存限制: 128 MB提交: 6  解决: 3[提交][状态][答疑][寄存][题解] 题目描述 大 学里实行学分.每门课程都有一定的学分,学生只要选修了这门课并考核通过就能获得相应的学分.学生最后的学分是他选修的各门课的学分的总和. 每个学生都要选择规定数量的课程.其中有些课程可以直接选修,有些课程需要一定的基础知识,必须在选了其它的一些课程的基础上才能选修.例如,<数据结 构>必须在选修了<高级语言程序设计>之后才能选修.我们称&l

hdu 1561The more, The Better(树形dp&amp;01背包)

The more, The Better Time Limit: 6000/2000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others) Total Submission(s): 4949    Accepted Submission(s): 2918 Problem Description ACboy很喜欢玩一种战略游戏,在一个地图上,有N座城堡,每座城堡都有一定的宝物,在每次游戏中ACboy允许攻克M个城堡并获得里面的宝

树形DP codevs 1814 最长链

codevs 1814 最长链 时间限制: 1 s 空间限制: 256000 KB 题目等级 : 钻石 Diamond 题目描述 Description 现给出一棵N个结点二叉树,问这棵二叉树中最长链的长度为多少,保证了1号结点为二叉树的根. 输入描述 Input Description 输入的第1行为包含了一个正整数N,为这棵二叉树的结点数,结点标号由1至N. 接下来N行,这N行中的第i行包含两个正整数l[i], r[i],表示了结点i的左儿子与右儿子编号.如果l[i]为0,表示结点i没有左儿

poj 1463(树形DP)

Strategic game Time Limit: 2000MS   Memory Limit: 10000K Total Submissions: 7584   Accepted: 3518 Description Bob enjoys playing computer games, especially strategic games, but sometimes he cannot find the solution fast enough and then he is very sad