首页 > 其他分享 >题解 P4827【[国家集训队] Crash 的文明世界】

题解 P4827【[国家集训队] Crash 的文明世界】

时间:2024-09-13 10:02:19浏览次数:1  
标签:Crash 题解 rhs operator modint P4827 return sum const

从阶乘幂到斯特林数 - caijianhong - 博客园 (cnblogs.com)

题目描述

Crash 小朋友最近迷上了一款游戏——文明5 (Civilization V)。在这个游戏中,玩家可以建立和发展自己的国家,通过外交和别的国家交流,或是通过战争征服别的国家。

现在 Crash 已经拥有了一个 \(n\) 个城市的国家,这些城市之间通过道路相连。由于建设道路是有花费的,因此 Crash 只修建了 \(n-1\) 条道路连接这些城市,不过可以保证任意两个城市都有路径相通。

在游戏中,Crash 需要选择一个城市作为他的国家的首都,选择首都需要考虑很多指标,有一个指标是这样的:

\[S(i) = \sum_{j = 1}^{n}{\rm dist}(i, j) ^ k \]

其中 \(S(i)\) 表示第 \(i\) 个城市的指标值,\({\rm dist}(i, j)\) 表示第 \(i\) 个城市到第 \(j\) 个城市需要经过的道路条数的最小值,\(k\) 为一个常数且为正整数。

因此 Crash 交给你一个简单的任务:给出城市之间的道路,对于每个城市,输出这个城市的指标值,由于指标值可能会很大,所以你只需要输出这个数 \(\bmod\ 10007\) 的值。

对于 \(100 \%\) 的数据,\(1\le n\le 5\times 10^4\),\(1\le k\le 150\)。

solution

为什么这东西不能直接换根 dp?你考虑你想要的操作无非就是:给你一个集合 \(S\),用

\[\sum_{x\in S}x^k \]

求出

\[\sum_{x\in S}(x+1)^k \]

然而假如你只管 \(k\) 次方的信息,你减一下就会发现

\[(x+1)^k-x^k=\sum_{i=0}^{k-1}\binom k i x^i \]

这个东西是二项式定理形式,展开以后有 \(k\) 项,都比 \(k\) 次要低。这 \(k\) 项你都要维护,维护一项需要 \(O(k)\),合着你需要总共 \(O(k^2)\) 的时间维护信息,过不去。


\((x+1)^k-x^k\) 是一个形如差分的东西,回想起《具体数学》第二章中介绍的有限微积分,其定义

\[\Delta f(x)=f(x+1)-f(x) \]

并发现 \(\Delta x^k\) 没有很好的形式,但是 \(\Delta x^\underline k\) 有

\[\Delta x^\underline k=(x+1-(x-k+1))x^\underline {k-1}=kx^\underline {k-1} \]

因为我们熟知下降幂与普通幂的转换

\[x^n=\sum_{k}\begin{Bmatrix}n\\ k\end{Bmatrix}x^\underline k \]

加一个求和符号不会影响什么东西

\[\sum_{x\in S}x^n=\sum_{k}\begin{Bmatrix}n\\ k\end{Bmatrix}\sum_{x\in S}x^\underline k \]

那我们就维护 \(\sum_{x\in S}x^\underline k\) 好了,就是所有 \(\leq k\) 次下降阶乘幂都维护一下,这样就能实现我们想要的东西了

\[\sum_{x\in S}(x+1)^\underline k=k\sum_{x\in S}x^\underline {k-1}+\sum_{x\in S}x^\underline k \]

可以 \(O(k)\) 维护出集合中所有数字加一后的信息,可以换根 dp。


\(f_{u, k}\) 维护了 \(\sum_{x\in subtree(u)}x^\underline k\)。记 \(A(f_u)\) 表示将所维护的所有 \(x\) 加一。

转移:\(f_u=\sum_v A(f_v)\),再加上自己的一个 \(0\)(意思是 \(0^\underline k=[k=0]\))。

换根:假设 \(f_u\) 现在拿着是全局的信息,那么将 \(A(f_u-A(f_v))\) 加到 \(f_v\) 上,然后递归 \(v\)。

code

#include <bits/stdc++.h>
using namespace std;
#ifdef LOCAL
#define debug(...) fprintf(stderr, ##__VA_ARGS__)
#else
#define endl "\n"
#define debug(...) void(0)
#endif
using LL = long long;
template <class T>
using must_int = enable_if_t<is_integral<T>::value, int>;
template <unsigned umod>
struct modint {/*{{{*/
  static constexpr int mod = umod;
  unsigned v;
  modint() : v(0) {}
  template <class T, must_int<T> = 0>
  modint(T x) {
    x %= mod;
    v = x < 0 ? x + mod : x;
  }
  modint operator+() const { return *this; }
  modint operator-() const { return modint() - *this; }
  friend int raw(const modint &self) { return self.v; }
  friend ostream& operator<<(ostream& os, const modint &self) { 
    return os << raw(self); 
  }
  modint &operator+=(const modint &rhs) {
    v += rhs.v;
    if (v >= umod) v -= umod;
    return *this;
  }
  modint &operator-=(const modint &rhs) {
    v -= rhs.v;
    if (v >= umod) v += umod;
    return *this;
  }
  modint &operator*=(const modint &rhs) {
    v = v * rhs.v % umod;
    return *this;
  }
  modint &operator/=(const modint &rhs) {
    assert(rhs.v);
    return *this *= qpow(rhs, mod - 2);
  }
  template <class T, must_int<T> = 0>
  friend modint qpow(modint a, T b) {
    modint r = 1;
    for (; b; b >>= 1, a *= a)
      if (b & 1) r *= a;
    return r;
  }
  friend modint operator+(modint lhs, const modint &rhs) { return lhs += rhs; }
  friend modint operator-(modint lhs, const modint &rhs) { return lhs -= rhs; }
  friend modint operator*(modint lhs, const modint &rhs) { return lhs *= rhs; }
  friend modint operator/(modint lhs, const modint &rhs) { return lhs /= rhs; }
  bool operator==(const modint &rhs) const { return v == rhs.v; }
  bool operator!=(const modint &rhs) const { return v != rhs.v; }
};/*}}}*/
using mint = modint<10007>;
int n, k;
mint S[210][210];
valarray<mint> a[50010];
vector<int> g[50010];
valarray<mint> add1(valarray<mint> f) {
  for (int i = k; i >= 1; i--) f[i] += f[i - 1] * i;
  return f;
}
mint getValue(const valarray<mint>& f) {
  mint ret = 0;
  for (int i = 0; i <= k; i++) ret += S[k][i] * f[i];
  return ret;
}
void dfs(int u, int fa) {
  a[u][0] = 1;
  for (int v : g[u]) if (v != fa) {
    dfs(v, u);
    a[u] += add1(a[v]);
  }
}
void chr(int u, int fa) {
  for (int v : g[u]) if (v != fa) {
    a[v] += add1(a[u] - add1(a[v]));
    chr(v, u);
  }
}
int main() {
#ifndef LOCAL
  cin.tie(nullptr)->sync_with_stdio(false);  
#endif
  cin >> n >> k;
  S[0][0] = 1;
  for (int i = 1; i <= k; i++) {
    for (int j = 1; j <= i; j++) S[i][j] = S[i - 1][j] * j + S[i - 1][j - 1];
  }
  for (int i = 1; i <= n; i++) a[i].resize(k + 1);
  for (int i = 1, u, v; i < n; i++) cin >> u >> v, g[u].push_back(v), g[v].push_back(u);
  dfs(1, 0);
  chr(1, 0);
  for (int i = 1; i <= n; i++) cout << getValue(a[i]) << endl;
  return 0;
}

标签:Crash,题解,rhs,operator,modint,P4827,return,sum,const
From: https://www.cnblogs.com/caijianhong/p/18411685/solution-P4827

相关文章

  • 题解:CF1767E Algebra Flash
    可以在cnblogs中阅读。\(m\le40\)的数据范围提示让我们往颜色种类上考虑。由题每次可以跳\(1\)或\(2\)格,即存在一条从\(1\)到\(n\)的路径的充要条件是不存在两个相邻的未激活格。换句话说,对任意两个相邻的格子都必须选择至少一个激活。任意两个,至少一个,这样的条件......
  • 洛谷P10504 守卫者的挑战 题解 概率DP
    题目链接:https://www.luogu.com.cn/problem/P10504状态\(f_{i,s,k}\)表示:当前正面临第\(i\)项挑战(此时第\(1\simi-1\)项挑战已完成,第\(i\)项挑战还没开始);目前已经挑战成功了\(s\)项(即第\(1\simi-1\)项挑战中共有\(s\)项挑战成功,\((i-1)-s\)项没挑战成功);......
  • [ARC101E] Ribbons on Tree 题解
    [ARC101E]RibbonsonTree题解其实算一道好题了。首先考虑不相关的simple的dp。平凡的想法是设\(dp_{i,j}\)表示\(i\)子树内有\(j\)个点还需要向上转移的方案数。转移式大概是个\(dp_{x,i+j}=dp_{y,i+j-1}+(dp_{p,i-1}+dp_{q,j-1})\)之类的东西。这样的dp是\(O(......
  • 题解 力扣 LeetCode 105 从前序与中序遍历序列构造二叉树 C/C++
    题目传送门:105.从前序与中序遍历序列构造二叉树-力扣(LeetCode)https://leetcode.cn/problems/construct-binary-tree-from-preorder-and-inorder-traversal/description/每次在中序遍历序列片段中找到前序遍历序列队首,这是该层的根节点该位置左侧是左子树,右侧是右子树再......
  • P3267 [JLOI2016/SHOI2016] 侦察守卫 题解
    P3267[JLOI2016/SHOI2016]侦察守卫题解\(n\le5\times10^5,D\le20\)的数据范围显然想到\(O(nd)\)的树形dp。考虑\(d\)这一维的状态设计。考虑\(i\)子树中的情况分为全部被覆盖和未全部被覆盖两种。对于第一种,显然我们要考虑子树中能向上覆盖影响的点的个数,于是设......
  • P11030 『DABOI Round 1』Blessings Repeated题解
    P11030『DABOIRound1』BlessingsRepeated题解【形式化题意】给定一个正整数\(k\)和两个字符串\(S,T\)。设字符串\(s\)为\(k\)个字符串\(S\)首尾相接得到的字符串,\(n=\verts\vert,m=\vertT\vert\)。设答案集合\(P=\{(i_0,i_1,\dots,i_{m-1})\mid0\lei......
  • CTS2024 题解
    \(\text{ByDaiRuiChen007}\)D1T1.水镜ProblemLink给定\(a_1\sima_n\),求多少个\([l,r]\)满足存在实数\(L\),将若干个\(a_i\)变成\(2L-a_i\)后\(a_l\sima_r\)严格递增。数据范围:\(n\le10^6\)。考虑钦定\(i-1\)翻转/不翻转,\(i\)翻转/不翻转,容易发现......
  • 【题解】Solution Set - NOIP2024集训Day27 树形 dp
    【题解】SolutionSet-NOIP2024集训Day27树形dphttps://www.becoder.com.cn/contest/5521「HDU4661」MessagePassing「BZOJ3935」Rbtree「ARC101E」RibbonsonTree「AGC034E」CompleteCompress「COCI2014.10」Kamp「SCOI2015」小凸玩密室「AGC008F」Black......
  • LOJ4222 「IOI2024」马赛克上色 题解
    题目描述给定长为\(n\)、下标从零开始的\(01\)序列\(x,y\),保证\(x_0=y_0\)。令\(col_{0,j}=x_j,col_{i,0}=y_i\),对\(\forall1\lei\ltn,1\lej\ltn\),\(col_{i,j}=[col_{i-1,j}=0\andcol_{i,j-1}=0]\)。\(q\)次询问,给定\(u,d,l,r\),求\(\sum_{i=u}^d......
  • Road of the King 题解
    RoadoftheKing题解形成强连通图的必要条件是点\(1\)能在环中,于是考虑\(1\)号点形成的强连通分量\(x\)。这类图论计数题目往往考虑dp,于是我们设\(dp_{i,j,k}\)表示走了\(i\)步,经过了\(j\)个点,\(1\)号点形成的强连通分量\(x\)的大小为\(k\)时的方案数。转移......