\(\mathcal{Description}\)

  Link.

  A B 两人在树上博弈,初始时有一枚棋子在结点 \(1\)。由 A 先操作,两人轮流移动沿树上路径棋子,且满足本次移动的树上距离严格大于上次的,无法移动者负。先给定一棵含 \(n\) 个结点的树,求包含结点 \(1\) 且使得 B 必胜的联通块数量。

  \(n\le2\times10^5\)。

\(\mathcal{Solution}\)

  结论对了正解写了细节萎了暴力分都没了 qwq……

  结论:联通块满足条件,当且仅当其中最深的叶子们不同时属于结点 \(1\) 的某棵子树。当然也能说作,结点 \(1\) 是联通块直径的中点。

证:略。

  “不同时属于”不太优美,考虑用所有方案减去非法方案。对于结点 \(1\) 的每棵子树,分别做长剖求出 \(f(v,d)\) 表示以 \(v\) 为根,最深叶子深度恰为 \(d\) 的联通块个数。随后钦定某棵子树取 \(d\),其余子树取严格小于 \(d\) 的方案,即能求出答案。

  复杂度 \(\mathcal O(n)\)(其实我写得比较难看,会有一个求逆元的 \(\log\) awa……)

\(\mathcal{Code}\)

/* Clearink */

#include <list>
#include <cstdio> #define rep( i, l, r ) for ( int i = l, rep##i = r; i <= rep##i; ++i )
#define per( i, r, l ) for ( int i = r, per##i = l; i >= per##i; --i ) typedef std::pair<int, int> PII;
#define fi first
#define se second inline int rint() {
int x = 0, s = getchar();
for ( ; s < '0' || '9' < s; s = getchar() );
for ( ; '0' <= s && s <= '9'; s = getchar() ) x = x * 10 + ( s ^ '0' );
return x;
} const int MAXN = 2e5, MOD = 998244353;
int n, ecnt, head[MAXN + 5];
int mxd[MAXN + 5], son[MAXN + 5];
PII *top, pool[MAXN * 5 + 10], *f[MAXN + 5];
// second 是乘法标记,转移过程中涉及到对长链的后缀乘法,需要打标记。
// 其实呢,由于出题人*****,更新时直接遍历长链也能过√
struct Edge { int to, nxt; } graph[MAXN * 2];
std::list<int> rt; inline int sub( int a, const int b ) { return ( a -= b ) < 0 ? a + MOD : a; }
inline int add( int a, const int b ) { return ( a += b ) < MOD ? a : a - MOD; }
inline int mul( const long long a, const int b ) { return int( a * b % MOD ); }
inline int mpow( int a, int b ) {
int ret = 1;
for ( ; b; a = mul( a, a ), b >>= 1 ) ret = mul( ret, b & 1 ? a : 1 );
return ret;
} inline void link( const int u, const int v ) {
graph[++ecnt] = { v, head[u] }, head[u] = ecnt;
graph[++ecnt] = { u, head[v] }, head[v] = ecnt;
} inline PII* alloc( const int len ) {
PII* ret = top; top += len + 3;
return ret;
} inline int init( const int u, const int fa ) {
int ret = 1;
mxd[u] = son[u] = 0;
for ( int i = head[u], v; i; i = graph[i].nxt ) {
if ( ( v = graph[i].to ) != fa ) {
ret = mul( ret, add( init( v, u ), 1 ) );
if ( mxd[u] < mxd[v] + 1 ) mxd[u] = mxd[son[u] = v] + 1;
}
}
// printf( "! all(%d)=%d\n", u, ret );
return ret;
} inline void pushad( const int u, const int i, const int v ) {
f[u][i].fi = mul( f[u][i].fi, v );
if ( f[u][i].se ) f[u][i].se = mul( f[u][i].se, v );
else f[u][i].se = v;
} inline void pushdn( const int u, const int i ) {
if ( f[u][i].se ) {
if ( i < mxd[u] ) pushad( u, i + 1, f[u][i].se );
f[u][i].se = 0;
}
} inline void solve( const int u, const int fa, PII* curf ) {
f[u] = curf != NULL ? curf : alloc( mxd[u] + 1 );
f[u][0].fi = 1;
if ( son[u] ) solve( son[u], u, f[u] + 1 );
pushdn( u, 0 );
for ( int i = head[u], v; i; i = graph[i].nxt ) {
if ( ( v = graph[i].to ) != fa && v != son[u] ) {
solve( v, u, NULL );
int vpre = 0, upre = 1;
rep ( j, 0, mxd[v] ) {
pushdn( v, j ), pushdn( u, j + 1 );
if ( j <= mxd[v] ) vpre = add( vpre, f[v][j].fi );
int t = f[u][j + 1].fi;
f[u][j + 1].fi = add( mul( upre, f[v][j].fi ),
mul( f[u][j + 1].fi, add( vpre, 1 ) ) );
upre = add( upre, t );
}
if ( mxd[v] + 2 <= mxd[u] ) {
pushad( u, mxd[v] + 2, add( vpre, 1 ) );
}
}
}
} inline void allClear() {
// mxd, son cleared in init.
ecnt = 0;
rep ( i, 1, n ) head[i] = 0;
for ( PII *p = pool; p <= top; *p++ = {} );
top = pool;
} int main() {
freopen( "game.in", "r", stdin );
freopen( "game.out", "w", stdout ); for ( int T = rint(); T--; ) {
n = rint(), allClear();
rep ( i, 2, n ) link( rint(), rint() ); int all = init( 1, 0 ), del = 0;
for ( int i = head[1], v; i; i = graph[i].nxt ) {
solve( v = graph[i].to, 1, NULL );
rt.push_back( v );
rep ( j, 0, mxd[v] - 1 ) pushdn( v, j );
} // printf( "! all=%d\n", all ); int las = 1;
rep ( i, 0, mxd[1] - 1 ) {
for ( int u: rt ) {
int k = !i ? 1 :
mul( las, mpow( add( f[u][i - 1].fi, 1 ), MOD - 2 ) );
del = add( del, mul( k, f[u][i].fi ) );
if ( i ) f[u][i].fi = add( f[u][i].fi, f[u][i - 1].fi );
}
for ( auto it( rt.begin() ); it != rt.end(); ) {
if ( i ) {
las = mul( las,
mpow( add( f[*it][i - 1].fi, 1 ), MOD - 2 ) );
}
las = mul( las, add( f[*it][i].fi, 1 ) );
if ( mxd[*it] == i ) it = rt.erase( it );
else ++it;
}
} printf( "%d\n", sub( all, del ) );
}
return 0;
}

\(\mathcal{Details}\)

  也不是说代码能力差吧,为什么我总能写那么长呢。

  赛时代码比较狰狞(?)可以理解,但尽量先细致地过一遍代码流程,尽量写下来,确认无误再动手。

Solution -「多校联训」博弈的更多相关文章

  1. Solution -「多校联训」取石子游戏

    \(\mathcal{Description}\)   Link.   有 \(n\) 堆石子,第 \(i\) 堆有 \(x_i\) 个,Alice 每次只能从这堆中拿走 \(a_i\) 个石子,Bo ...

  2. Solution -「多校联训」排水系统

    \(\mathcal{Description}\)   Link.   在 NOIP 2020 A 的基础上,每条边赋权值 \(a_i\),随机恰好一条边断掉,第 \(i\) 条段的概率正比于 \(a ...

  3. Solution -「多校联训」I Love Random

    \(\mathcal{Description}\)   给定排列 \(\{p_n\}\),可以在其上进行若干次操作,每次选取 \([l,r]\),把其中所有元素变为原区间最小值,求能够得到的所有不同序 ...

  4. Solution -「多校联训」签到题

    \(\mathcal{Description}\)   Link.   给定二分图 \(G=(X\cup Y,E)\),求对于边的一个染色 \(f:E\rightarrow\{1,2,\dots,c\ ...

  5. Solution -「多校联训」朝鲜时蔬

    \(\mathcal{Description}\)   Link.   破案了,朝鲜时蔬 = 超现实树!(指写得像那什么一样的题面.   对于整数集 \(X\),定义其 好子集 为满足 \(Y\sub ...

  6. Solution -「多校联训」消失的运算符

    \(\mathcal{Description}\)   Link.   给定长度为 \(n\) 的合法表达式序列 \(s\),其中数字仅有一位正数,运算符仅有 - 作为占位.求将其中恰好 \(k\) ...

  7. Solution -「多校联训」假人

    \(\mathcal{Description}\)   Link.   一种物品有 长度 和 权值 两种属性,现给定 \(n\) 组物品,第 \(i\) 组有 \(k_i\) 个,分别为 \((1,a ...

  8. Solution -「多校联训」古老的序列问题

    \(\mathcal{Description}\)   Link.   给定序列 \(\{a_n\}\),和 \(q\) 次形如 \([L,R]\) 的询问,每次回答 \[\sum_{[l,r]\su ...

  9. Solution -「多校联训」Sample

    \(\mathcal{Description}\)   Link   (稍作简化:)对于变量 \(p_{1..n}\),满足 \(p_i\in[0,1],~\sum p_i=1\) 时,求 \(\ma ...

随机推荐

  1. 详谈 Java工厂 --- 静态工厂 【简单工厂模式】

    1.前言 什么是工厂模式? 就是为了尽可能将代码的耦合度降低而产生的设计模式. 这篇随笔讲解静态工厂的思路和具体操作. 2.总结 (1)静态工厂又称 简单 工厂模式 ,是最最简单的工厂模式. (2)优 ...

  2. 日志收集系统系列(四)之LogAgent优化

    实现功能 logagent根据etcd的配置创建多个tailtask logagent实现watch新配置 logagent实现新增收集任务 logagent删除新配置中没有的那个任务 logagen ...

  3. linux笔记(一)

    linux 开源镜像网址:http://mirrors.163.com pwd : 展示当前所在的目录的绝对路径 cd : 切换到某个路径  cd 命令,是 Change Directory 的缩写, ...

  4. 基本的sql语法

    1. SELECT: 用于从数据库中选取数据 SELECT name,value FROM table_name 2.SELECT DISTINCT 语句用于返回唯一不同的值(去重) 3.WHERE ...

  5. test_5 排序‘+’、‘-’

    题目是:有一组"+"和"-"符号,要求将"+"排到左边,"-"排到右边,写出具体的实现方法. 方法一: l=['-', ...

  6. dubbo 实现简易分布式服务

    dubbo 实现简易分布式服务 服务器需要搭建zookeeper环境 zookeeper端口2181 还需要有java环境 1.需求 某个电商系统,订单服务需要调用用户服务获取某个用户的所有地址: 我 ...

  7. 网络编程-TCP连接的建立与终止

    TCP是一个面向连接的协议.无论哪一方向另一方发送数据之前,都必须先在双方之间建立一条连接. 1.建立连接 请求端(通常称为客户)发送一个 SYN 段指明客户打算连接的服务器的端口,以及初始序号( I ...

  8. 单片机main函数退出后发生什么——以stm32为例

    STM32:main函数退出后发生什么? 我们都在说单片机要运行在无限循环里,不能退出,可退出之后会发生什么? 讨论STM32启动过程的文章数不胜数,可main函数结束之后会发生什么却少有讨论. 几日 ...

  9. [µC/GUI 学习]µC/GUI移植

    一.什么是µC/GUI µC/GUI为任何需要图形显示器的嵌入式应用提供了一种灵活的图形用户界面(GUI).µC/GUI允许软件工程师在使用了LCD显示器的产品上增加美轮美奂的用户界面,从简单的2D黑 ...

  10. C# 同步 异步 回调 状态机 async await Demo

    源码 https://gitee.com/s0611163/AsyncAwaitDemo 为什么会研究这个? 我们项目的客户端和服务端通信用的是WCF,我就想,能不能用异步的方式调用WCF服务呢?或者 ...