bzoj2243: [SDOI2011]染色

虚树-树上动态规划的利器 虚树 问题引入 在一类树上动态规划问题中,题目给出的询问往往包含树上的很多各节点,并保证总的点数规模小于某个值. 如果我们直接在整颗树上进行dpdpdp的话,时间复杂度与询问的次数有关,这显然是不可接受的,如果我们可以找到一种动态规划的方法,使其时间复杂度与询问中点的实际规模相关就好了. 于是虚树应运而生. 虚树概念 虚树即是一颗虚拟构建的一棵树,这个树只包含关键点以及关键lc... 阅读详情

2243: [SDOI2011]染色

Time Limit: 20 Sec   Memory Limit: 512 MB
Submit: 3271   Solved: 1262
[ Submit][ Status][ Discuss]

Description

给定一棵有n个节点的无根树和m个操作,操作有2类:

1、将节点a到节点b路径上所有点都染成颜色c

2、询问节点a到节点b路径上的颜色段数量(连续相同颜色被认为是同一段),如“1122213段组成:“11、“222和“1

请你写一个程序依次完成这m个操作。

Input

第一行包含2个整数nm,分别表示节点数和操作数;

第二行包含n个正整数表示n个节点的初始颜色

下面 行每行包含两个整数xy,表示xy之间有一条无向边。

下面 行每行描述一个操作:

“C a b c”表示这是一个染色操作,把节点a到节点b路径上所有点(包括ab)都染成颜色c

“Q a b”表示这是一个询问操作,询问节点a到节点b(包括ab)路径上的颜色段数量。

Output

对于每个询问操作,输出一行答案。

Sample Input

6 5

2 2 1 2 1 1

1 2

1 3

2 4

2 5

2 6

Q 3 5

C 2 1 1

Q 3 5

C 5 1 2

Q 3 5

Sample Output

3

1

2

HINT

数N<=10^5,操作数M<=10^5,所有的颜色C为整数且在[0, 10^9]之间。

Source



思路:这题维护比较复杂,LCT和树链剖分都可以写,树链剖分还是好写一些。

如果写树链剖分的话,线段树里记录左右端点颜色,颜色个数和覆盖标记,update时判断对接处的两点颜色是否相同,相同颜色数就等于左半部分+右半部分-1。树链剖分统计答案时也一样,记录左右端点颜色,相同就减1.

如果写LCT,splay记录的东西和树链剖分差不多,但update时要合并两次,上半段和自己及自己和下半段。标记也有两种,一种是把一个点设为根是的翻转,一种是覆盖。

记得开long long...

树链剖分代码:

#include<cstdio>
#include<cstring>
#include<algorithm>
#define ll long long
#define ls p<<1
#define rs ((p<<1)|1)
using namespace std;
const ll maxm=200010,maxn=100010,maxt=maxn<<3;
ll n,m,pre[maxm],now[maxn],son[maxm],col[maxn],tot,root;
ll fa[maxn],hson[maxn],w[maxn],top[maxn],dep[maxn],size[maxn],T,q,a[maxn];
char s[10];
void add(ll a,ll b){pre[++tot]=now[a],now[a]=tot,son[tot]=b;}
struct node{ll lcol,rcol,val,cov;};
struct Tree{
	node t[maxt];
	void update(ll p){
		t[p].lcol=t[ls].lcol;
		t[p].rcol=t[rs].rcol;
		t[p].val=t[ls].val+t[rs].val-(t[ls].rcol==t[rs].lcol);
	}
	void down(ll p){
		if (t[p].cov){
			t[ls].cov=t[rs].cov=t[p].cov;
			t[ls].lcol=t[ls].rcol=t[rs].lcol=t[rs].rcol=t[p].cov;
			t[ls].val=t[rs].val=1;
			t[p].cov=0;
		}
	}
	void build(ll p,ll l,ll r){
		if (l==r){
			t[p].lcol=t[p].rcol=a[l],t[p].val=1;
			return;
		}
		ll mid=(l+r)>>1;
		build(ls,l,mid),build(rs,mid+1,r);
		update(p);
	}
	void change(ll p,ll l,ll r,ll a,ll b,ll v){
		if (l==a&&r==b){
			t[p].val=1,t[p].lcol=t[p].rcol=t[p].cov=v;
//			printf("%d %d %d %d %d\n",l,r,a,b,v);
			return;
		}
		ll mid=(l+r)>>1;
		down(p);
		if (b<=mid) change(ls,l,mid,a,b,v);
		else if (a>mid) change(rs,mid+1,r,a,b,v);
		else change(ls,l,mid,a,mid,v),change(rs,mid+1,r,mid+1,b,v);
		update(p);
	}
	node query(ll p,ll l,ll r,ll a,ll b){
		if (a==l&&b==r) return t[p];//printf("%d %d %d %d %d\n",l,r,a,b,t[p].val),
		ll mid=(l+r)>>1;
		down(p);//printf("%d %d %d %d %d\n",p,l,r,a,b);
		if (b<=mid) return query(ls,l,mid,a,b);
		else if (a>mid) return query(rs,mid+1,r,a,b);
		else{
			node tmp,t1=query(ls,l,mid,a,mid),t2=query(rs,mid+1,r,mid+1,b);
			tmp.lcol=t1.lcol,tmp.rcol=t2.rcol;
			tmp.val=t1.val+t2.val-(t1.rcol==t2.lcol);
			return tmp;
		}
	}
}Seg;

void dfs(ll x){
	size[x]=1,hson[x]=0;
	for (ll y=now[x];y;y=pre[y])
		if (son[y]!=fa[x]){
			fa[son[y]]=x,dep[son[y]]=dep[x]+1,dfs(son[y]);
			if (size[son[y]]>size[hson[x]]) hson[x]=son[y];
			size[x]+=size[son[y]];
		}
}

void btree(ll x,ll tp){
	w[x]=++m,a[m]=col[x],top[x]=tp;
	if (hson[x]) btree(hson[x],top[x]);
	for (ll y=now[x];y;y=pre[y])
		if (son[y]!=fa[x]&&son[y]!=hson[x])
			btree(son[y],son[y]);
}

void cover(ll a,ll b,ll c){
	ll f1=top[a],f2=top[b];
	while (f1!=f2){
		if (dep[f1]<dep[f2]) swap(f1,f2),swap(a,b);
		Seg.change(1,1,m,w[f1],w[a],c);
		a=fa[f1],f1=top[a];
	}
	if (dep[a]>dep[b]) swap(a,b);
//	printf("%d %d %d\n",w[a],w[b],c);
	Seg.change(1,1,m,w[a],w[b],c);
}

ll answer(ll a,ll b){
	ll f1=top[a],f2=top[b],acol=-1,bcol=-1,ans=0;//分别表a端和b端接口的颜色
	node tmp;
	while (f1!=f2){
		if (dep[f1]<dep[f2]) swap(f1,f2),swap(acol,bcol),swap(a,b);
//		printf("%d %d\n",a,f1);
		tmp=Seg.query(1,1,m,w[f1],w[a]);
//		printf("%d\n",tmp.val);
		ans+=tmp.val-(acol==tmp.rcol);//因为a在f1下,所以应该是a与这一段链的右颜色对接
		a=fa[f1],f1=top[a],acol=tmp.lcol;
	}
	if (dep[a]<dep[b]) swap(a,b),swap(acol,bcol);
	tmp=Seg.query(1,1,m,w[b],w[a]);//printf("tmp%d\n",tmp.val);
	ans+=tmp.val-(acol==tmp.rcol)-(bcol==tmp.lcol);
	return ans;
}

int main(){
	scanf("%d%d",&n,&q);root=1;
	for (ll i=1;i<=n;i++) scanf("%d",&col[i]),col[i]++;
	for (ll i=1,a,b;i<=n-1;i++) scanf("%d%d",&a,&b),add(a,b),add(b,a);
	dfs(root),btree(root,root),Seg.build(1,1,m);
//	for (ll i=1;i<=n;i++) printf("%d %d %d %d\n",i,hson[i],top[i],col[i]);
	for (ll i=1,a,b,c;i<=q;i++){
		scanf("%s",s);
		if (s[0]=='C') scanf("%d%d%d",&a,&b,&c),c++,cover(a,b,c);
		else scanf("%d%d",&a,&b),printf("%d\n",answer(a,b));
	}
	return 0;
}



SDOI2011】消耗战 题目 题目描述 在一场战争中,战场由 nn 个岛屿和 n-1n−1 个桥梁组成,保证每两个岛屿间有且仅有一条路径可达。现在,我军已经侦查到敌军的总部在编号为 11 的岛屿,而且他们已经没有足够多的能源维系战斗,我军胜利在望。已知在其他 kk 个岛屿上有丰富能源,为了防止敌军获取能源,我军的任务是炸毁一些桥梁,使得敌军不能到达任何能源丰富的岛屿。由于不同桥梁的材质和结构不同,所以炸毁不同的桥梁有不同... 阅读详情

相关推荐

数据结构专题——虚树

虚树(virtual tree)的概念 虚树 是将一个树的点集的某一个子集,以及该子集中点的 LCALCALCA 的集合,一起所重构出来的一棵树 虚树的用途 在树型dp中,有时候没必要对整颗树进行dp,只用对某个子集构成的虚树进行dp,大大降低了时空复杂度 例题:P2495 [SDOI2011] 消耗战 [SDOI2011] 消耗战 题目描述 在一场战争中,战场由 nnn 个岛屿和 n−1n-1n−1 个桥梁组成,保证每两个岛屿间有且仅有一条路径可达。现在,我军已经侦查到敌军的总部在编号为 111 的岛屿,

m0_52766855的博客 3065

SDOI2011 总结

文章目录2867【SDOI2011】打地鼠2868【SDOI2011】计算器2869【SDOI2011染色2941【SDOI2011】工作安排3324【SDOI2011】黑白棋3325【SDOI2011】消防3328【SDOI2011】保密 2867【SDOI2011】打地鼠 注意到rrr和ccc无关(r⋅r\cdotr⋅c的锤子相当于用r⋅1r\cdot1r⋅1的锤子砸ccc次,ccc同理) ...

qq_42835841的博客 605

数论——Baby Step Giant Step大步小步算法

bsgs算法 Baby Step Giant Step算法,简称BSGS算法,也称为大步小步算法. 解决对象 离散对数:当x≡Gk(modm)x≡Gk(modm)x\equiv G^k\pmod m时,logG(x)≡k(modϕ(m))logG(x)≡k(modϕ(m))log_G(x)\equiv k\pmod{\phi(m)}。此处的logG(x)logG(x)log_G(x)是x...

蒟蒻柴犬首相的博客 4384

bzoj 2243: [SDOI2011]染色

bzoj 2243: [SDOI2011]染色树链剖分,线段树题意给定一棵有n个节点的无根树和m个操作,操作有2类: 将节点a到节点b路径上所有点都染成颜色c; 询问节点a到节点b路径上的颜色段数量(连续相同颜色被认为是同一段),如“112221”由3段组成:“11”、“222”和“1”。 请你写一个程序依次完成这m个操作。思路这种东西。。感觉很套路啊,学会了之后根本不难写,顶多是代码太多写丑了需要

P.H.S 307

BZOJ2243: [SDOI2011]染色

BZOJ2243: [SDOI2011]染色 Description 给定一棵有n个节点的无根树和m个操作,操作有2类: 1、将节点a到节点b路径上所有点都染成颜色c; 2、询问节点a到节点b路径上的颜色段数量(连续相同颜色被认为是同一段), 如“112221”由3段组成:“11”、“222”和“1”。 请你写一个程序依次完成这m个操作。 Input 第一行包含2个整数...

weixin_30955341的博客 72

BZOJ 2243: [SDOI2011]染色树链剖分

2243: [SDOI2011]染色 Time Limit: 20 Sec  Memory Limit: 512 MB Submit: 4577  Solved: 1709 [Submit][Status][Discuss] Description 给定一棵有n个节点的无根树和m个操作,操作有2类: 1、将节点a到节点b路径上所有点都染成颜色c; 2、询问节点

世界很大 584

BZOJ 2243: [SDOI2011]染色

BZOJ 2243: [SDOI2011]染色题目Description给定一棵有n个节点的无根树和m个操作,操作有2类: 1、将节点a到节点b路径上所有点都染成颜色c; 2、询问节点a到节点b路径上的颜色段数量(连续相同颜色被认为是同一段), 如“112221”由3段组成:“11”、“222”和“1”。 请你写一个程序依次完成这m个操作。 Input第一行包含2个整数n和m,分别表示节点

faojie的博客 320

BZOJ 2243: [SDOI2011]染色 树链剖分

2243: [SDOI2011]染色 Time Limit: 20 SecMemory Limit: 512 MBSubmit: 1886Solved: 752[Submit][Status] Description 给定一棵有n个节点的无根树和m个操作,操作有2类: 1、将节点a到节点b路径上所有点都染成颜色c; 2、询问节点a到节点b路径上的颜色段数量(连续...

alex151194310的博客 104

BZOJ - 2243: [SDOI2011]染色 树链剖分

2243: [SDOI2011]染色 Time Limit:20 SecMemory Limit:512 MBSubmit:10456Solved:4013[Submit][Status][Discuss] Description 给定一棵有n个节点的无根树和m个操作,操作有2类: 1、将节点a到节点b路径上所有点都染成颜色c; 2、询问节点a到节点...

diangan7073的博客 173

BZOJ-2243: [SDOI2011]染色-树链剖分

http://www.lydsy.com/JudgeOnline/problem.php?id=2243 Description 给定一棵有n个节点的无根树和m个操作,操作有2类: 1、将节点a到节点b路径上所有点都染成颜色c; 2、询问节点a到节点b路径上的颜色段数量(连续相同颜色被认为是同一段),如“112221”由3段组成:“11”、“222”和“

缺氧 920

BZOJ 2243: [SDOI2011]染色 树链剖分

Description 给定一棵有n个节点的无根树和m个操作,操作有2类: 1、将节点a到节点b路径上所有点都染成颜色c; 2、询问节点a到节点b路径上的颜色段数量(连续相同颜色被认为是同一段),如“112221”由3段组成:“11”、“222”和“1”。 请你写一个程序依次完成这m个操作。 Input 第一行包含2个整数n和m,分别表示节点数和操作数; 第二行包含n个正

I'm Growing 438

Bzoj2243[SDOI2011]染色:树链剖分

题目链接:2243: [SDOI2011]染色 一眼就知道是个树剖…… 对于线段树上每个区间,我们维护最左边的颜色、最右边的颜色、总颜色段数 合并区间的时候父区间的颜色段数=左右儿子的颜色段数和-左儿子最右边的颜色是否和有儿子最左边的颜色相等 然而我写炸了一上午QAQ 指针的线段树等于号写成了减号真是看不出来QAQ 令人鸡冻的带代码: #include #include #inclu

蜜の夜明け 416

BZOJ2243:[SDOI2011]染色

这题的区间维护比较麻烦,顺便复习了一下区间合并 维护区间间隔色段数,跨链时更新一下上一条链顶的颜色,去重 #include //#pragma comment(linker, "/STACK:1024000000,1024000000") #include #include #include #include #include #include #include #include #incl

Dream in the Night 314
上一篇: bzoj2819: Nim
下一篇: bzoj2594: [Wc2006]水管局长数据加强版
thy_asdf
博客等级 码龄12年 16粉丝 186原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值