【PKUSC2019】树染色【线段树合并】【树形DP】
Description
给出一棵n个点的树,现在有m种颜色,要给每个节点染色,相邻节点不能同色。
另外有k条限制,形如x号点不能为颜色y
同一节点有可能有多条限制。
求方案数对998244353取模的结果。
n<=200000,m<=1e9,k<=400000
Solution
考场上一直在想怎么容斥做,怎么都弄不出来。
学傻了。
考虑暴力DP
设\(f[i][j]\)为当前处理了以i为根的子树,i的颜色为j的方案数。
记\(g[i]=\sum\limits_{k}f[i][k]\)
显然有转移$$f[i][j]=[!ban[i][j]]\prod_{p\in son[i]}(g[p]-f[p][j])$$
但是这样的状态数是\(n*m\)的,我们发现只需要记下子树中有的颜色,其他的颜色的答案都是一样的。
这样状态数缩减到\(n*k\),但还是很大,于是我们考虑采用线段树来维护。
转移的时候我们将子树一个个的合并到根
大概是\(f[i][j]=(g[p]-f[p][j])*f[i][j]\)
根据这个我们就可以线段树合并了。
如果只有父亲有,就直接乘
儿子父亲都有暴力合并
只有儿子有的话把括号拆开,就是乘上\(-f[i][j]\)加上\(g[p]*f[i][j]\)
需要维护区间乘区间加,类似一次函数维护即可。
时间复杂度大概是\(O((n+k)\log m)\),具体可以看代码。
Code
写了个对拍没问题,姑且当它是对的吧
#include <bits/stdc++.h>
#define fo(i,a,b) for(int i=a;i<=b;++i)
#define fod(i,a,b) for(inti i=a;i>=b;--i)
#define N 200005
#define M 13000005
#define LL long long
#define mo 998244353
using namespace std;
int n,m,l,fs[N],nt[2*N],dt[2*N],m1;
vector<int> qs[N];
LL ksm(LL k,LL n)
{
LL s=1;
for(;n;n>>=1,k=k*k%mo) if(n&1) s=s*k%mo;
return s;
}
int n1,t[M][2],sz[M],rt[N];
LL sp[M],g[N],f[N],lz[M][2];
void nwp(int &k)
{
if(!k) k=++n1,lz[k][0]=1,lz[k][1]=0;
}
void ins(int k,int l,int r,int x,int v)
{
if(l==r) {sp[k]=0,sz[k]=1;return;}
int mid=(l+r)>>1;
if(x<=mid) nwp(t[k][0]),ins(t[k][0],l,mid,x,v);
else nwp(t[k][1]),ins(t[k][1],mid+1,r,x,v);
sp[k]=(sp[t[k][0]]+sp[t[k][1]]);
if(sp[k]>=mo) sp[k]-=mo;
sz[k]=sz[t[k][0]]+sz[t[k][1]];
}
LL gp,fp,fk,vs;
void upd(int k,LL u,LL v)
{
sp[k]=(u*sp[k]+v*sz[k])%mo;
lz[k][0]=lz[k][0]*u%mo;
lz[k][1]=(lz[k][1]*u%mo+v)%mo;
}
void down(int k)
{
if(lz[k][0]!=1||lz[k][1]!=0)
{
if(t[k][0]) upd(t[k][0],lz[k][0],lz[k][1]);
if(t[k][1]) upd(t[k][1],lz[k][0],lz[k][1]);
lz[k][0]=1,lz[k][1]=0;
}
}
void mrg(int &k,int x,int l,int r)
{
if(!k)
{
if(!x) return;
k=x,upd(k,mo-fk,gp*fk%mo);
return;
}
if(!x) {upd(k,(gp-fp+mo)%mo,0);return;}
if(l==r) {sp[k]=(gp-sp[x]+mo)%mo*sp[k]%mo,sz[k]=sz[k]|sz[x];return;}
int mid=(l+r)>>1;
down(k),down(x);
mrg(t[k][0],t[x][0],l,mid);
mrg(t[k][1],t[x][1],mid+1,r);
sp[k]=(sp[t[k][0]]+sp[t[k][1]])%mo;
sz[k]=sz[t[k][0]]+sz[t[k][1]];
}
void dfs(int k,int fa)
{
f[k]=1;
nwp(rt[k]);
int r=qs[k].size();
fo(j,0,r-1) ins(rt[k],1,m,qs[k][j],0);
for(int i=fs[k];i;i=nt[i])
{
int p=dt[i];
if(p!=fa)
{
dfs(p,k);
gp=g[p],fk=f[k],fp=f[p];
mrg(rt[k],rt[p],1,m);
f[k]=(g[p]-f[p]+mo)%mo*f[k]%mo;
}
}
g[k]=(f[k]*(LL)(m-sz[rt[k]])%mo+sp[rt[k]])%mo;
}
void link(int x,int y)
{
nt[++m1]=fs[x];
dt[fs[x]=m1]=y;
}
int main()
{
cin>>n>>m>>l;
fo(i,1,n-1)
{
int x,y;
scanf("%d%d",&x,&y);
link(x,y),link(y,x);
}
fo(i,1,l)
{
int x,y;
scanf("%d%d",&x,&y);
qs[x].push_back(y);
}
dfs(1,0);
printf("%lld\n",g[1]);
}
【PKUSC2019】树染色【线段树合并】【树形DP】的更多相关文章
- 【BZOJ5210】最大连通子块和 树剖线段树+动态DP
[BZOJ5210]最大连通子块和 Description 给出一棵n个点.以1为根的有根树,点有点权.要求支持如下两种操作: M x y:将点x的点权改为y: Q x:求以x为根的子树的最大连通子块 ...
- [CF1007D]Ants[2-SAT+树剖+线段树优化建图]
题意 我们用路径 \((u, v)\) 表示一棵树上从结点 \(u\) 到结点 \(v\) 的最短路径. 给定一棵由 \(n\) 个结点构成的树.你需要用 \(m\) 种不同的颜色为这棵树的树边染色, ...
- UVALive 7148 LRIP【树分治+线段树】
题意就是要求一棵树上的最长不下降序列,同时不下降序列的最小值与最大值不超过D. 做法是树分治+线段树,假设树根是x,y是其当前需要处理的子树,对于子树y,需要处理出两个数组MN,MX,MN[i]表示以 ...
- BZOJ4317Atm的树&BZOJ2051A Problem For Fun&BZOJ2117[2010国家集训队]Crash的旅游计划——二分答案+动态点分治(点分树套线段树/点分树+vector)
题目描述 Atm有一段时间在虐qtree的题目,于是,他满脑子都是tree,tree,tree…… 于是,一天晚上他梦到自己被关在了一个有根树中,每条路径都有边权,一个神秘的声音告诉他,每个点到其他的 ...
- dfs序+主席树 或者 树链剖分+主席树(没写) 或者 线段树套线段树 或者 线段树套splay 或者 线段树套树状数组 bzoj 4448
4448: [Scoi2015]情报传递 Time Limit: 20 Sec Memory Limit: 256 MBSubmit: 588 Solved: 308[Submit][Status ...
- 线段树简单入门 (含普通线段树, zkw线段树, 主席树)
线段树简单入门 递归版线段树 线段树的定义 线段树, 顾名思义, 就是每个节点表示一个区间. 线段树通常维护一些区间的值, 例如区间和. 比如, 上图 \([2, 5]\) 区间的和, 为以下区间的和 ...
- 【UOJ#388】【UNR#3】配对树(线段树,dsu on tree)
[UOJ#388][UNR#3]配对树(线段树,dsu on tree) 题面 UOJ 题解 考虑一个固定区间怎么计算答案,把这些点搞下来建树,然后\(dp\),不难发现一个点如果子树内能够匹配的话就 ...
- [BZOJ 3110] [luogu 3332] [ZJOI 2013]k大数查询(权值线段树套线段树)
[BZOJ 3110] [luogu 3332] [ZJOI 2013]k大数查询(权值线段树套线段树) 题面 原题面有点歧义,不过从样例可以看出来真正的意思 有n个位置,每个位置可以看做一个集合. ...
- bzoj 3110 [Zjoi2013]K大数查询——线段树套线段树(标记永久化)
题目:https://www.lydsy.com/JudgeOnline/problem.php?id=3110 第一道线段树套线段树! 第一道标记永久化! 为什么为什么写了两个半小时啊…… 本想线段 ...
- [BZOJ 1901] Dynamic Rankings 【树状数组套线段树 || 线段树套线段树】
题目链接:BZOJ - 1901 题目分析 树状数组套线段树或线段树套线段树都可以解决这道题. 第一层是区间,第二层是权值. 空间复杂度和时间复杂度均为 O(n log^2 n). 线段树比树状数组麻 ...
随机推荐
- java中利用hanlp比较两个文本相似度的步骤
使用 HanLP - 汉语言处理包 来处理,他能处理很多事情,如分词.调用分词器.命名实体识别.人名识别.地名识别.词性识别.篇章理解.关键词提取.简繁拼音转换.拼音转换.根据输入智能推荐.自定义分词 ...
- Oracle表的Rowid字段
Rowid 字段类型: Rowid 是一行数据的一个唯一标识. ROWID 是数据的详细地址,通过 rowid,oracle 可以快速的定位某行具体的数据的位置. ROWID 可以分为物理 rowid ...
- CSP 俄罗斯方块(201604-2)
问题描述 俄罗斯方块是俄罗斯人阿列克谢·帕基特诺夫发明的一款休闲游戏. 游戏在一个15行10列的方格图上进行,方格图上的每一个格子可能已经放置了方块,或者没有放置方块.每一轮,都会有一个新的由4个小方 ...
- Python进阶编程 类与类的关系
类与类的关系 依赖关系 # 依赖关系: 将一个类的类名或者对象传给另一个类的方法中. class Elephant: def __init__(self, name): self.name = nam ...
- Sleepy Game CodeForces - 936B
大意: 给定有向图, 初始点S, 两个人轮流移动, 谁不能移动则输, 但后手睡着了, 先手可以控制后手操作, 求最后先手结果. 刚开始看错了, 还以为后手也是最优策略.... 实际上判断是否有偶数个节 ...
- Mysql学习(一)之简单介绍
数据库简介 数据库分类 关系型数据库:MySQL.Oracle.SQLServer.Access.db2.fox pro 文件型数据库:sqlite.mongodb 空间型数据库: 数据库分为两端 数 ...
- luogu题解 P1099 【树网的核】树的直径变式+数据结构维护
题目链接: https://www.luogu.org/problemnew/show/P1099 https://www.lydsy.com/JudgeOnline/problem.php?id=1 ...
- 删库?半个DBA的跑路经验总结
0. 国内呆不下了,赶紧出国 首先,不要选动车,要选最近的一班飞机,尽快出国,能走高速走高速,不然选人少的路线. 没错,我们 DBA 都是常备护照的. 切记,注意看高德地图实时路况. 我们有个前辈就是 ...
- scala下划线的作用
https://stackoverflow.com/questions/8000903/what-are-all-the-uses-of-an-underscore-in-scala Existent ...
- 第十篇.6、python并发编程之IO模型
一 IO模型介绍 为了更好地了解IO模型,我们需要事先回顾下:同步.异步.阻塞.非阻塞 同步(synchronous) IO和异步(asynchronous) IO,阻塞(blocking) IO和非 ...