题目描述

这棵树上有n个节点,由n−1条树枝相连。初始时树上都挂了一个毒瘤,颜色为ci。接下来Salamander将会进行q个操作。

Salamander有时会修改树上某个点到另外一个点的简单路径上所有毒瘤的颜色。

对于给定的树上某个点集S,Salamander还定义了某个点的权值:

\(W_i=\sum_{j\in S}T(i,j)\)

其中T(i,j)表示i到j的路径上毒瘤颜色的段数,比如i到j的路径上毒瘤颜色为1223312时,颜色段数为5。

Salamander对树上的毒瘤们的状态很感兴趣,所以有时会指定树上m个节点作为点集,询问这m个节点的权值。

输入输出格式

输入格式:

第一行包括两个正整数n、q,表示树上的节点数和操作个数。

第二行包括用空格隔开的n个正整数ci,表示树上每个节点初始的毒瘤颜色。

接下来n−1行,每行两个正整数u、v,表示树上有一条连接u和v的边。

接下来描述q个操作:

  • 1 若给出的第一个整数等于1,那么接下来将会有三个正整数u、v、y,表示将树上编号为u的点到编号为v的点的简单路径上的毒瘤颜色全都改为y。
  • 2 若给出的第一个整数等于2,那么接下来将会有一个正整数m,表示指定的树上节点个数。下一行将会有m个用空格隔开的互不相同的正整数,表示当前询问给定的m个节点。

输出格式:

若干行,对于每个2操作输出对应的答案。

Solution

树剖维护修改。

对于询问,很容易想到虚树。

对于虚树上的边,边权定义为去掉\(dep\)小的那个点所在的颜色块剩余的块的数量。

这样的话,一条路径的权值就是边权之和\(+1\)。

所以,建完虚树之后,直接二次换根\(dp\)就好了。

(当然你也可以点分治,但是那样代码大概会破300行。。)

所以这玩意就是一模板大集合。。

#include<bits/stdc++.h>
using namespace std; void read(int &x) {
x=0;int f=1;char ch=getchar();
for(;!isdigit(ch);ch=getchar()) if(ch=='-') f=-f;
for(;isdigit(ch);ch=getchar()) x=x*10+ch-'0';x*=f;
} void print(int x) {
if(x<0) putchar('-'),x=-x;
if(!x) return ;print(x/10),putchar(x%10+48);
}
void write(int x) {if(!x) putchar('0');else print(x);putchar('\n');} const int maxn = 2e5+10; int n,col[maxn],m,sz[maxn],dfn[maxn],f[maxn],dep[maxn],re[maxn]; #define ls p<<1
#define rs p<<1|1
#define mid ((l+r)>>1) struct data {
int lcol,rcol,sum;
data operator + (const data &rhs) const {
if(!sum) return rhs;
if(!rhs.sum) return *this;
return (data){lcol,rhs.rcol,sum+rhs.sum-(rcol==rhs.lcol)};
}
}; struct Segment_Tree {
int cov[maxn<<2];
data t[maxn<<2];
void push_tag(int p,int x) {t[p].sum=1,t[p].lcol=t[p].rcol=x,cov[p]=x;}
void pushdown(int p) {
if(!cov[p]) return ;
push_tag(ls,cov[p]),push_tag(rs,cov[p]);
cov[p]=0;
}
void modify(int p,int l,int r,int x,int y,int z) {
if(x<=l&&r<=y) return push_tag(p,z),void();
pushdown(p);
if(x<=mid) modify(ls,l,mid,x,y,z);
if(y>mid) modify(rs,mid+1,r,x,y,z);
t[p]=t[ls]+t[rs];
}
data query(int p,int l,int r,int x,int y) {
if(x<=l&&r<=y) return t[p];
pushdown(p);
if(y<=mid) return query(ls,l,mid,x,y);
else if(x>mid) return query(rs,mid+1,r,x,y);
else return query(ls,l,mid,x,y)+query(rs,mid+1,r,x,y);
}
void build(int p,int l,int r) {
if(l==r) return t[p].sum=1,t[p].lcol=t[p].rcol=col[re[l]],void();
build(ls,l,mid),build(rs,mid+1,r),t[p]=t[ls]+t[rs];
}
}SGT; struct Heavy_Light_Decomposation {
int head[maxn],tot,hs[maxn],top[maxn],dfn_cnt;
struct edge{int to,nxt;}e[maxn<<1]; void add(int u,int v) {e[++tot]=(edge){v,head[u]},head[u]=tot;}
void ins(int u,int v) {add(u,v),add(v,u);} void dfs(int x,int fa) {
sz[x]=1,f[x]=fa,dep[x]=dep[fa]+1;
for(int i=head[x];i;i=e[i].nxt)
if(e[i].to!=fa) {
dfs(e[i].to,x);sz[x]+=sz[e[i].to];
if(sz[e[i].to]>sz[hs[x]]) hs[x]=e[i].to;
}
} void dfs2(int x) {
if(hs[f[x]]==x) top[x]=top[f[x]];
else top[x]=x;
dfn[x]=++dfn_cnt;re[dfn_cnt]=x;
if(hs[x]) dfs2(hs[x]);
for(int i=head[x];i;i=e[i].nxt)
if(e[i].to!=f[x]&&e[i].to!=hs[x]) dfs2(e[i].to);
} int lca(int x,int y) {
while(top[x]!=top[y]) {
if(dep[top[x]]<dep[top[y]]) swap(x,y);
x=f[top[x]];
}if(dep[x]>dep[y]) swap(x,y);return x;
} void modify(int x,int y,int z) {//puts("OK");
while(top[x]!=top[y]) {
if(dep[top[x]]<dep[top[y]]) swap(x,y);
SGT.modify(1,1,n,dfn[top[x]],dfn[x],z);
x=f[top[x]];
}if(dep[x]>dep[y]) swap(x,y);
SGT.modify(1,1,n,dfn[x],dfn[y],z);
} void print(data x) {printf("Print :: %d %d %d\n",x.sum,x.lcol,x.rcol);} int query(int x,int t) {
data ans;int bo=1;
while(top[x]!=top[t]) {
data res=SGT.query(1,1,n,dfn[top[x]],dfn[x]);
if(bo) bo=0,ans=res;else ans=res+ans;
x=f[top[x]];
}
data res=SGT.query(1,1,n,dfn[t],dfn[x]);
if(bo) bo=0,ans=res;else ans=res+ans;
return ans.sum;
} void debug(int x,int fa) {
//printf("Debug :: %d %d\n",x,SGT.query(1,1,n,dfn[x],dfn[x]).lcol);
for(int i=head[x];i;i=e[i].nxt)
if(e[i].to!=fa) debug(e[i].to,x);
} }HLD; int cmp(int x,int y) {return dfn[x]<dfn[y];} int id[maxn]; int cmp2(int x,int y) {return id[x]<id[y];} struct Virtual_Tree {
int head[maxn],tot,c[maxn],f[maxn],g[maxn],vis[maxn];
struct edge{int to,nxt,w;}e[maxn<<1]; void add(int u,int v,int w) {e[++tot]=(edge){v,head[u],w},head[u]=tot;}
void ins(int u,int v,int w) {add(u,v,w),add(v,u,w);} int in[maxn],k,use[maxn],used,sta[maxn],top; void build() {
sta[++top]=1,use[++used]=1;
for(int i=1;i<=k;i++) {
if(in[i]==1) continue;
int t=HLD.lca(in[i],sta[top]),pre=-1;
while(dfn[sta[top]]>dfn[t]&&dfn[sta[top]]<dfn[t]+sz[t]) {
if(pre!=-1) ins(sta[top],pre,HLD.query(pre,sta[top])-1);
pre=sta[top],use[++used]=sta[top],top--;
}
if(pre!=-1) ins(t,pre,HLD.query(pre,t)-1);
if(sta[top]!=t) sta[++top]=t;
sta[++top]=in[i];
}
int pre=-1;
while(top) {
if(pre!=-1) ins(sta[top],pre,HLD.query(pre,sta[top])-1);
pre=sta[top],use[++used]=sta[top],top--;
}
} void dfs(int x,int fa) {
if(vis[x]) c[x]=1;else c[x]=0;
for(int i=head[x];i;i=e[i].nxt)
if(e[i].to!=fa) {
//printf("Dfs :: %d %d %d\n",x,e[i].to,e[i].w);
dfs(e[i].to,x);c[x]+=c[e[i].to];
f[x]+=e[i].w*c[e[i].to]+f[e[i].to];
}
} void clear() {
for(int i=1;i<=used;i++) {
int t=use[i];
head[t]=id[t]=vis[t]=f[t]=g[t]=c[t]=0;
}tot=used=top=0;
} void dfs2(int x,int fa) {
for(int i=head[x];i;i=e[i].nxt)
if(e[i].to!=fa) {
int v=e[i].to;
g[v]=g[x]-e[i].w*c[v]+e[i].w*(k-c[v]);
dfs2(e[i].to,x);
}
} void solve() {
read(k);for(int i=1;i<=k;i++) read(in[i]),vis[in[i]]=1,id[in[i]]=i;
sort(in+1,in+k+1,cmp);build();
dfs(1,0);g[1]=f[1];dfs2(1,0);
sort(in+1,in+k+1,cmp2);
for(int i=1;i<=k;i++) printf("%d ",g[in[i]]+k);puts("");
clear();
//printf("\n ------- \n");
}
}VT; int main() {
read(n),read(m);
for(int i=1;i<=n;i++) read(col[i]);
for(int i=1,x,y;i<n;i++) read(x),read(y),HLD.ins(x,y);
HLD.dfs(1,0),HLD.dfs2(1),SGT.build(1,1,n);
for(int i=1;i<=m;i++) {
int op,u,v,y;read(op);
if(op==1) read(u),read(v),read(y),HLD.modify(u,v,y);
else VT.solve();
}
//write(HLD.query(7,1));
//HLD.debug(1,0);
return 0;
}

[luogu4242] 树上的毒瘤的更多相关文章

  1. 洛谷P4242 树上的毒瘤

    解:首先有个套路是一条边的权值是[两端点颜色不同].这个用树剖直接维护,支持修改. 每次询问建虚树,查询虚树上每条边的权值.然后树形DP,用开店的方法,每个点链加链查. #include <bi ...

  2. luoguP4242树上的毒瘤

    传送门 模板集合吧,除了码农,没啥难的... 和bzoj2243:[SDOI2011]染色十分相像,但是多了点集和查询的区别 然后点集显然可以看出是虚树问题,查询可以用点分治\(O(nlogn)\), ...

  3. Noip前的大抱佛脚----赛前任务

    赛前任务 tags:任务清单 前言 现在xzy太弱了,而且他最近越来越弱了,天天被爆踩,天天被爆踩 题单不会在作业部落发布,所以可(yi)能(ding)会不及时更新 省选前的练习莫名其妙地成为了Noi ...

  4. 洛谷P4426 毒瘤 [HNOI/AHOI2018] 虚树+树上dp

    正解:虚树+树上dp 解题报告: 传送门! 首先解释一下题意趴,,,语文70pts选手已经开始看不懂题辣QAQ 大概就是个给一个图,求独立集方案,且保证图是联通的,边的数量最多只比点多10 首先思考如 ...

  5. 【HNOI 2018】毒瘤

    Problem Description 从前有一名毒瘤. 毒瘤最近发现了量产毒瘤题的奥秘.考虑如下类型的数据结构题:给出一个数组,要求支持若干种奇奇怪怪的修改操作(例如给一个区间内的数同时加上 \(c ...

  6. 「HNOI2016」数据结构大毒瘤

    真是 \(6\) 道数据结构毒瘤... 开始口胡各种做法... 「HNOI2016」网络 整体二分+树状数组. 开始想了一个大常数 \(O(n\log^2 n)\) 做法,然后就被卡掉了... 发现直 ...

  7. 【BZOJ5287】[HNOI2018]毒瘤(动态规划,容斥)

    [BZOJ5287][HNOI2018]毒瘤(动态规划,容斥) 题面 BZOJ 洛谷 题解 考场上想到的暴力做法是容斥: 因为\(m-n\le 10\),所以最多会多出来\(11\)条非树边. 如果就 ...

  8. [Codeforces743D][luogu CF743D]Chloe and pleasant prizes[树状DP入门][毒瘤数据]

    这个题的数据真的很毒瘤,身为一个交了8遍的蒟蒻的呐喊(嘤嘤嘤) 个人认为作为一个树状DP的入门题十分合适,同时建议做完这个题之后再去做一下这个题 选课 同时在这里挂一个选取节点型树形DP的状态转移方程 ...

  9. 「HNOI2018」毒瘤

    「HNOI2018」毒瘤 解题思路 先考虑只有一棵树的情况,经典独立集计数. \[ dp[u][0]=\prod (dp[v][0]+dp[v][1]) \\ dp[u][1]=\prod dp[v] ...

随机推荐

  1. Java 单词 day seven

    Constructor Constructor Constructor Constructor Constructor Constructor Constructor Constructor Cons ...

  2. TLS握手协议

    SSL/TLS基础 SSL(Secure Sockets Layer 安全套接层),及其继任者-传输层安全(Transport Layer Security, TLS)是为网络通信提供安全及数据完整性 ...

  3. LeetCode46. Permutations

    Given a collection of distinct integers, return all possible permutations. Example: Input: [1,2,3] O ...

  4. 【树链剖分 ODT】cf1137F. Matches Are Not a Child's Play

    孔爷的杂题系列:LCT清新题/ODT模板题 题目大意 定义一颗无根树的燃烧序列为:每次选取编号最小的叶子节点形成的序列. 要求支持操作:查询一个点$u$在燃烧序列中的排名:将一个点的编号变成最大 $n ...

  5. ubuntu18.04.1LTS系统远程工具secureCRT

    ubuntu18.04.1LTS类windows的系统下安装远程管理工具 本地电脑之前安装的是win10,疲于win10频繁的更新和各种兼容问题,果断放弃win10系统,安装了Ubuntu 18.04 ...

  6. css样式 body的font-size 为什么用625%

    浏览器的默认高度?一般为16px. 为什么用62.5%作为body的默认样式?16px62.5%=10px.* 那么为什么一般多是 16px  *625% = 100px; <响应式Web设计实 ...

  7. get请求中文乱码问题

    Get中文乱码解决 Get请求类型: <form action="${pageContext.request.contextPath}/addArtical.action"  ...

  8. 正则表达式re.S的用法

    正则表达式re.S的用法 在Python的正则表达式中,有一个参数为re.S.它表示"."(不包含外侧双引号,下同)的作用扩展到整个字符串,包括"\n".看如下 ...

  9. C语言结构体篇 结构体

    在描述一个物体的属性的时候,单一的变量类型是无法完全描述完全的.所以有了灵活的结构体变量. 结构体变量从意义上来讲是不同数据类型的组合,从内存上来讲是在一个空间内进行不同的大小划分. 1.1 结构体类 ...

  10. 状压DP详解(位运算)

    前言: 状压DP是一种非常暴力的做法(有一些可以排除某些状态的除外),例如dp[S][v]中,S可以代表已经访问过的顶点的集合,v可以代表当前所在的顶点为v.S代表的就是一种状态(二进制表示),比如 ...