【LOJ】#3044. 「ZJOI2019」Minimax 搜索
LOJ#3044. 「ZJOI2019」Minimax 搜索
一个菜鸡的50pts暴力
设\(dp[u][j]\)表示\(u\)用\(j\)次操作能使得\(u\)的大小改变的方案数
设每个点的初始答案是\(S[u]\)
每个数大小只和\(S[1]\)的大小关系有关
于是每个数的状态设为-1(比S[1]小),1(比S[1]大),0(和S[1]一样)
状态里设的改变是指在这三种状态里的一种变为另一种
如果\(S[u] == S[1]\)或者\(u\)点取max但是\(S[u] < S[1]\),\(u\)点取min但是\(S[u] > S[1]\)
这个时候子树里任意一个点改变\(u\)就会改变,统计两个序列的后缀和并且和当前位置更新即可(因为操作数选最小的那个)
否则的话就是子树里所有与\(S[u] < S[1]\)真假性不同的点都必须改变,剩下的点选了也不用变,统计前缀和并且和当前位置更新(因为操作数选最大的那个)
复杂度\(O(n^2)\)
神仙的100pts正解
我们统计前缀操作,也就是对于一个\(i\)统计\(pre[i]\)是小于等于\(i\)次操作的方案数
我们找出答案都是\(S[1]\)的那条链,容易发现这条链上奇数位置的点挂着的子树,其中的叶子都必须尽量改大(大于\(S[1]\)),而偶数位置的点挂着的子树里面的叶子,都必须尽量改小(小于\(S[1]\))
改成大于\(S[1]\)的最少操作是改成\(S[1] + 1\),小于\(S[1]\)的最少操作是改成\(S[1] - 1\)
以下把\(<S[1 ]\)标成0,$ >S[1]$标成1
于是设\(dp[i][0 / 1]\)表示第\(i\)个点选了一个点集后\(i\)的答案是0或者是1
转移的话就是设\(t = dep[i] \& 1\)
$dp[u][t \oplus 1] = \prod dp[v][t \oplus 1] $
\(dp[u][t] = 2^{leaf[u]} - dp[u][t \oplus 1]\)
最后我们要求的是链上的点不变的方案数,再用总的方案数减去
我们可以只记一个,而我们要用的是\(dp[u][t]\)于是我们只记\(dp[u][t]\)
从小到大枚举最小的操作数,每次可以至多有两个点变为可以更改
发现这个式子可以动态dp转移,于是我们设常数项是\(2^{leaf[u]}\),系数是\(-\prod dp[v][t \oplus 1]\)其中v是\(u\)所有的轻儿子
而叶子节点的值,在变更的时候看一下是否初始值是\(S[u] < S[1]\)而且是需要尽量大的子树(\(S[u] > S[1]\)而且需要尽量小的子树),这个时候可以把叶子节点的常数项改成1,否则叶子节点的值就不需要改变了
#include <bits/stdc++.h>
#define fi first
#define se second
#define pii pair<int,int>
#define mp make_pair
#define pb push_back
#define space putchar(' ')
#define enter putchar('\n')
#define eps 1e-10
#define MAXN 200005
#define ba 47
//#define ivorysi
using namespace std;
typedef long long int64;
typedef unsigned int u32;
typedef double db;
template<class T>
void read(T &res) {
res = 0;T f = 1;char c = getchar();
while(c < '0' || c > '9') {
if(c == '-') f = -1;
c = getchar();
}
while(c >= '0' && c <= '9') {
res = res * 10 +c - '0';
c = getchar();
}
res *= f;
}
template<class T>
void out(T x) {
if(x < 0) {x = -x;putchar('-');}
if(x >= 10) {
out(x / 10);
}
putchar('0' + x % 10);
}
const int MOD = 998244353;
int inc(int a,int b) {
return a + b >= MOD ? a + b - MOD : a + b;
}
int mul(int a,int b) {
return 1LL * a * b % MOD;
}
void update(int &a,int b) {
a = inc(a,b);
}
int fpow(int x,int c) {
int res = 1,t = x;
while(c) {
if(c & 1) res = mul(res,t);
t = mul(t,t);
c >>= 1;
}
return res;
}
struct Edge {
int to,next;
}E[MAXN * 2];
struct val {
int v,zero;
val(int a = 0) {
v = max(a,1);zero = !a;
}
friend val operator * (const val &a,const val &b) {
val c;
c.v = mul(a.v,b.v);
c.zero = a.zero + b.zero;
return c;
}
friend val operator / (const val &a,const val &b) {
val c;
c.v = mul(a.v,fpow(b.v,MOD - 2));
c.zero = a.zero - b.zero;
return c;
}
int getval() {
if(zero) return 0;
else return v;
}
}ms[MAXN],all;
struct node {
int l,r;pair<int,int> rec;
}tr[MAXN * 4];
int head[MAXN],sumE,N,L,R;
int dfn[MAXN],siz[MAXN],son[MAXN],dep[MAXN],idx,lef[MAXN],fa[MAXN];
int top[MAXN],bot[MAXN],line[MAXN];
int S[MAXN],ans[MAXN];
int dp[MAXN][2];
bool nd[MAXN];
void add(int u,int v) {
E[++sumE].to = v;
E[sumE].next = head[u];
head[u] = sumE;
}
void dfs(int u) {
dep[u] = dep[fa[u]] + 1;
if(dep[u] % 2 == 0) S[u] = N;
siz[u] = 1;
for(int i = head[u] ; i ; i = E[i].next) {
int v = E[i].to;
if(v != fa[u]) {
fa[v] = u;
dfs(v);
siz[u] += siz[v];
if(dep[u] & 1) S[u] = max(S[u],S[v]);
else S[u] = min(S[u],S[v]);
if(siz[v] > siz[son[u]]) son[u] = v;
}
}
if(!son[u]) {
S[u] = u;return;
}
}
void dfs2(int u,int p = 0) {
if(S[u] != S[1]) {
dfn[u] = ++idx;
if(!top[u]) top[u] = u;
line[idx] = u;
}
for(int i = head[u] ; i ; i = E[i].next) {
int v = E[i].to;
if(v != fa[u]) {
if(S[u] == S[1] && S[1] == S[v]) son[u] = v;
}
}
if(!son[u]) {
lef[u]++;
bot[u] = u;
nd[u] = p;
if(S[u] == S[1]) {dp[u][0] = 1;ms[u] = 1;}
else {
if(S[u] > S[1]) dp[u][1] = 2;
else dp[u][0] = 2;
}
return;
}
top[son[u]] = top[u];
dfs2(son[u],p);
bot[u] = bot[son[u]];
lef[u] += lef[son[u]];
if(S[u] == S[1]) p = dep[u] & 1;
ms[u] = 1;int t = dep[u] & 1;
for(int i = head[u] ; i ; i = E[i].next) {
int v = E[i].to;
if(v != fa[u] && v != son[u]) {
dfs2(v,p);
lef[u] += lef[v];
ms[u] = ms[u] * dp[v][t ^ 1];
}
}
dp[u][t ^ 1] = mul(ms[u].getval(),dp[son[u]][t ^ 1]);
dp[u][t] = inc(fpow(2,lef[u]),MOD - dp[u][t ^ 1]);
if(S[u] == S[1]) all = all * ms[u];
}
pii Merge(pii a,pii b) {
pii c;
c.fi = mul(a.fi,b.fi);
c.se = inc(mul(a.fi,b.se),a.se);
return c;
}
void updateTr(int u) {
tr[u].rec = Merge(tr[u << 1].rec,tr[u << 1 | 1].rec);
}
void build(int u,int l,int r) {
tr[u].l = l;tr[u].r = r;
if(l == r) {
int t = line[l];
tr[u].rec.fi = (MOD - ms[t].getval()) % MOD;tr[u].rec.se = fpow(2,lef[t]);
if(!son[t]) tr[u].rec.se = dp[t][dep[t] & 1];
return;
}
int mid = (l + r) >> 1;
build(u << 1,l,mid);
build(u << 1 | 1,mid + 1,r);
updateTr(u);
}
void Change(int u,int pos) {
if(tr[u].l == tr[u].r) {
int t = line[pos];
if(!son[t]) {
if((S[t] > S[1]) != nd[t]) tr[u].rec.se = 1;
}
else tr[u].rec.fi = (MOD - ms[t].getval()) % MOD;
return;
}
int mid = (tr[u].l + tr[u].r) >> 1;
if(pos <= mid) Change(u << 1,pos);
else Change(u << 1 | 1,pos);
updateTr(u);
}
pii Query(int u,int l,int r) {
if(tr[u].l == l && tr[u].r == r) return tr[u].rec;
int mid = (tr[u].l + tr[u].r) >> 1;
if(r <= mid) return Query(u << 1,l,r);
else if(l > mid) return Query(u << 1 |1,l,r);
else return Merge(Query(u << 1,l,mid),Query(u << 1 | 1,mid + 1,r));
}
void Process(int u) {
while(S[u] != S[1]) {
int ori = Query(1,dfn[top[u]],dfn[bot[u]]).se;
Change(1,dfn[u]);
int f = fa[top[u]],nw = Query(1,dfn[top[u]],dfn[bot[u]]).se;
if(S[f] == S[1]) all = all / ms[f];
ms[f] = ms[f] / ori;
ms[f] = ms[f] * nw;
if(S[f] == S[1]) all = all * ms[f];
u = f;
}
}
void Init() {
read(N);read(L);read(R);
int a,b;
for(int i = 1 ; i < N ; ++i) {
read(a);read(b);
add(a,b);add(b,a);
}
}
void Solve() {
all = 1;
dfs(1);
dfs2(1);
ans[1] = fpow(2,lef[1] - 1);
build(1,1,idx);
int pw = fpow(2,lef[1]);
ans[N] = pw - 1;
for(int i = 2 ; i < N ; ++i) {
if(S[1] + 1 - i > 1 && !son[S[1] + 1 - i]) {Process(S[1] + 1 - i);}
if(S[1] - 1 + i <= N && !son[S[1] - 1 + i]) {Process(S[1] - 1 + i);}
ans[i] = inc(pw,MOD - all.getval());
}
for(int i = N ; i >= 1 ; --i) {
ans[i] = inc(ans[i],MOD - ans[i - 1]);
}
for(int i = L ; i <= R ; ++i) {
out(ans[i]);space;
}
enter;
}
int main() {
#ifdef ivorysi
freopen("f1.in","r",stdin);
#endif
Init();
Solve();
return 0;
}
【LOJ】#3044. 「ZJOI2019」Minimax 搜索的更多相关文章
- Loj #3044. 「ZJOI2019」Minimax 搜索
Loj #3044. 「ZJOI2019」Minimax 搜索 题目描述 九条可怜是一个喜欢玩游戏的女孩子.为了增强自己的游戏水平,她想要用理论的武器武装自己.这道题和著名的 Minimax 搜索有关 ...
- LOJ3044. 「ZJOI2019」Minimax 搜索
LOJ3044. 「ZJOI2019」Minimax 搜索 https://loj.ac/problem/3044 分析: 假设\(w(1)=W\),那么使得这个值变化只会有两三种可能,比\(W\)小 ...
- [LOJ#3044][动态DP]「ZJOI2019」Minimax 搜索
题目传送门 容易想到一种暴力 DP:先转化成对于每个 \(k\) 求出 \(\max_{i\in S}|i-w_i|\le k\) 的方案数,最后差分 然后问题转化成每个叶子的权值有个取值区间,注意这 ...
- loj#2537. 「PKUWC2018」Minimax
题目链接 loj#2537. 「PKUWC2018」Minimax 题解 设\(f_{u,i}\)表示选取i的概率,l为u的左子节点,r为u的子节点 $f_{u,i} = f_{l,i}(p \sum ...
- Loj #3045. 「ZJOI2019」开关
Loj #3045. 「ZJOI2019」开关 题目描述 九条可怜是一个贪玩的女孩子. 这天,她和她的好朋友法海哥哥去玩密室逃脱.在他们面前的是 \(n\) 个开关,开始每个开关都是关闭的状态.要通过 ...
- Loj #3042. 「ZJOI2019」麻将
Loj #3042. 「ZJOI2019」麻将 题目描述 九条可怜是一个热爱打麻将的女孩子.因此她出了一道和麻将相关的题目,希望这题不会让你对麻将的热爱消失殆尽. 今天,可怜想要打麻将,但是她的朋友们 ...
- 【线段树 树链剖分 差分 经典技巧】loj#3046. 「ZJOI2019」语言【未完】
还是来致敬一下那过往吧 题目分析 先丢代码 #include<bits/stdc++.h> ; ; ; struct node { int top,son,fa,tot; }a[maxn] ...
- 「ZJOI2019」Minmax搜索
传送门 Solution 叶子节点的变化区间是连续的,可得知非叶子节点的权值变化区间也是连续的 由此可知,\(W\)的变化值的可行域也是连续的,所以只需要看它能否变为\(W+1\)或\(W-1\) 对 ...
- @loj - 3043@「ZJOI2019」线段树
目录 @description@ @solution@ @accepted code@ @details@ @description@ 九条可怜是一个喜欢数据结构的女孩子,在常见的数据结构中,可怜最喜 ...
随机推荐
- JetBrains IDE 基本快捷键
转载自:https://nextfe.com/jetbrains-ide-shortcuts/ 一个好的手艺人很熟悉他的工具.软件开发者也不例外.所以,在编程的过程中,值得了解一些键盘快捷键,以免因为 ...
- Java异常Error和Exception
简述 程序运行时,发生了不被期望的结果,阻止了程序按照预期正常执行,这就是异常.世界上没有不出错的程序,只有正确处理好意外情况,才能保证程序的可靠性. Java 语言在设计之初就提供了相对完善的异常处 ...
- 7.RabbitMQ--消息确认机制(confirm)
RabbitMQ--消息确认机制(confirm) Confirm模式 RabbitMQ为了解决生成者不知道消息是否真正到达broker这个问题,采用通过AMQP协议层面为我们提供了事务机制方案,但是 ...
- 第七章 python基础之函数,递归,内置函数
五 局部变量和全局变量 name='cyj' #在程序的一开始定义的变量称为全局变量. def change_name(): global name #global 定义修改全局变量. name=&q ...
- H5页面验收流程及性能验收标准
1,接入方需要保证H5页面兼容性.功能正常以及满足H5约束规范 2,有支付功能的必须要有订单业务以及订单入口,存在有效订单 3,提前X个工作日提交验收,需要抄送相关设计.产品.H5性能验收负责人进行验 ...
- 使用Spring基于应用层实现读写分离(一)基础版
背景 我们一般应用对数据库而言都是“读多写少”,也就说对数据库读取数据的压力比较大,有一个思路就是说采用数据库集群的方案, 其中一个是主库,负责写入数据,我们称之为:写库: 其它都是从库,负责读取数据 ...
- SpringMVC 理论
应用系统三层架构: C/S:客户端/服务器 B/S:浏览器/服务器 标准分层: 表现层:WEB 层,接受结果,响应结果,分发请求:通常客户端使用 http 协议请求 web 层,web 层需要接受 h ...
- spark 常用函数介绍(python)
以下是个人理解,一切以官网文档为准. http://spark.apache.org/docs/latest/api/python/pyspark.html 在开始之前,我先介绍一下,RDD是什么? ...
- 3.JSON使用
把 JSON 文本转换为 JavaScript 对象 JSON 最常见的用法之一,是从 web 服务器上读取 JSON 数据(作为文件或作为 HttpRequest),将 JSON 数据转换为 Jav ...
- Android省电和提高效率
一.Android省电开发之性能优化 电量优化 Android应用开发中的网络.定位.传感器等都是比较耗电的特性,我们应该正确使用API来有效降低应用的耗电量. 1.BroadcastReceiver ...