Sunshine’s city(lct+线段树)

本文探讨了在一个由多个城市组成、道路构成树状结构的国度中,如何通过更新特定路径上的文化类型来最小化不同文化城市间移动的代价。介绍了使用线段树和链剖算法来高效解决此问题的方法。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Sunshine’s city(city)
【问题描述】
在很久很久之前Sunshine建立了一个n个城市的王国 (城市从0开始编号) ,其中0 号城市是Sunshine 居住的地方,也就是首都。追求完美的 Sunshine国王把整个王国的道路设计成了一棵树的形状, 两个城市之间有且只有一条道路能到达。Sunshine 王国是一个文化多元的国家。初始时,每个城市都有一中单独的文化。当居民在相邻的城市间移动时,如果这两个城市不是同一种文化,那么需要付出一个单位的代价。体恤国民的 Sunshine 想要减少居民的支出。具体说来,他会每次将首都到一个城市u 路径上的所有城市都发展成一种新的文化。因为这个原因,来往于城市间的代价会经常改变, 于是Sunshine 找你来帮忙。 给定一个城市 u, 定义 f(u)为以u 为根的子树中所有节点到根节点的代价的和。
【输入格式】
第一行有一个整数 n 表示城市的数目。 接下来 n-1 行每行两个整数Ai,Bi 表示一条连接这两点的道路。
接下来一行一个整数 m, 表示接下来有 m 组操作, 每组操作包含一个字符 t和一个整数 u。 如果 t='O',表示一个新的帮会占据了从首都到 u 路径上的城市。 如果 t='q',表示询问 f(u)。
【输出格式】
对于每组测试数据中的 t='q'类型的询问,输出一行一个整数表示 f(u)。
【样例输入】
13
0 1
0 2
1 11
1 10
1 9
9 12
2 5
5 8
2 4
2 3
4 6
4 7
7
q 0
O 4
q 6
q 2
O 9
q 9
q 2
【样例输出】
26
1
6
1
13
【数据规模及约定】
对于 30%的数据n,m<=1000
对于 60%的数据n,m<=50000
另外存在 20%的数据,树是一条链。
对于 100%的数据n,m<=200000


#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
#define N 400003
#define LL long long
using namespace std;
int n,m;
int fa[N],ch[N][3],rev[N],st[N],col[N],pre[N];
int point[N],v[N],next[N],l[N],r[N],deep[N],tot,cur[N];
LL tr[N*4],delta[N*4];
int q[N],cnt;
void add(int x,int y)
{
	tot++; next[tot]=point[x]; point[x]=tot; v[tot]=y;
	tot++; next[tot]=point[y]; point[y]=tot; v[tot]=x;
}
int isroot(int x)
{
	return (ch[fa[x]][0]!=x&&ch[fa[x]][1]!=x);
}
int get(int x)
{
	return ch[fa[x]][1]==x;
}
void rotate(int x)
{
    int y=fa[x]; int z=fa[y]; int which=get(x);
    if (!isroot(y))  ch[z][ch[z][1]==y]=x;
    ch[y][which]=ch[x][which^1]; fa[ch[x][which^1]]=y;
    ch[x][which^1]=y; fa[y]=x; fa[x]=z;
}
/*void pushdown(int x)
{
	if (rev[x])
	 {
	 	swap(ch[x][1],ch[x][0]);
	 	rev[ch[x][1]]^=1; rev[ch[x][0]]^=1;
	 	rev[x]=0;
	 }
}*/
void splay(int x)
{
	/*top=0; st[++top]=x;
	for (int i=x;!isroot(i);i=fa[x])
	 st[++top]=fa[i];
	for (int i=top;i>=1;i--)
	 pushdown(st[i]);*/
	while(!isroot(x))
	 {
	 	int y=fa[x];
	 	if (!isroot(y))
	 	 rotate(get(x)==get(y)?y:x);
	 	rotate(x);
	 }
}
void update(int x)
{
	tr[x]=tr[x<<1]+tr[x<<1|1];
}
void pushdown(int l,int r,int now)
{
	int mid=(l+r)/2;
	if (delta[now]==0)  return;
	delta[now<<1]+=delta[now]; delta[now<<1|1]+=delta[now];
	tr[now<<1]+=(LL)(mid-l+1)*delta[now]; tr[now<<1|1]+=(LL)(r-mid)*delta[now];
	delta[now]=0;
}
void qjchange(int now,int l,int r,int ll,int rr,int v)
{
	if (l>=ll&&r<=rr)
	{
		tr[now]+=(LL)(r-l+1)*(LL)v;
		delta[now]+=(LL)v;
		return;
	}
	pushdown(l,r,now);
	int mid=(l+r)/2;
	if (ll<=mid) qjchange(now<<1,l,mid,ll,rr,v);
	if (rr>mid) qjchange(now<<1|1,mid+1,r,ll,rr,v);
	update(now);
}
LL qjsum(int now,int l,int r,int ll,int rr)
{
	if (l>=ll&&r<=rr) return tr[now];
	pushdown(l,r,now);
	int mid=(l+r)/2;
	LL ans=0;
	if (ll<=mid) ans+=qjsum(now<<1,l,mid,ll,rr);
	if (rr>mid) ans+=qjsum(now<<1|1,mid+1,r,ll,rr);
	return ans;
}
int find(int x)
{
	while(ch[x][0]) x=ch[x][0];
	return x;
}
void access(int x,int k)
{
	int t=0;
	while (x)
	{
		col[x]=k;
		splay(x);
		int t2=find(ch[x][1]);
		if (t2) 
		  qjchange(1,1,n,l[t2],r[t2],1); 
		ch[x][1]=t;
		int t1=find(t);
		if (t1)  qjchange(1,1,n,l[t1],r[t1],-1);
		t=x; x=fa[x];
	}
}
void link(int x,int y)
{
	fa[x]=y;
}
/*void dfs(int x,int f)
{
	q[++cnt]=x;
	l[x]=cnt;
	for (int i=point[x];i;i=next[i])
	 if (v[i]!=f)
	  {
	  	dfs(v[i],x);
	  }
	r[x]=cnt;
}*/
void dfs1()
{
	int top=0;
	for (int i=1;i<=n;i++) cur[i]=point[i];
	st[++top]=1;  pre[1]=0;
	while(top)
	{
		int x=st[top];
		if (v[cur[x]]==pre[x])  cur[x]=next[cur[x]];
		if (!cur[x])
		 {
		 	--top;
		 	r[x]=cnt;
		 	continue;
		 }
		int vt=v[cur[x]];
		st[++top]=vt; pre[vt]=x; l[vt]=++cnt;
		cur[x]=next[cur[x]];
	}
}
int main()
{
	freopen("city.in","r",stdin);
	freopen("city.out","w",stdout);
	scanf("%d",&n);
	for (int i=1;i<n;i++)
	 {
	 	int x,y; scanf("%d%d",&x,&y);
	 	x++; y++;
	 	add(x,y); link(y,x);
	 }
	//dfs(1,0);
	dfs1();
	//for (int i=1;i<=n;i++)
	 //cout<<l[i]<<" "<<r[i]<<endl;
	for (int i=2;i<=n;i++)
	  qjchange(1,1,n,l[i],r[i],1);
	//cout<<qjsum(1,1,n,1,n)<<endl;
	scanf("%d",&m);
	int size=n;
	for (int i=1;i<=n;i++) col[i]=i;
	for (int i=1;i<=m;i++)
	 {
	 	char s[10]; int x; 
		scanf("%s%d",s+1,&x); x++;
	 	if (s[1]=='q')
	 	 printf("%I64d\n",qjsum(1,1,n,l[x],r[x]));
	    else 
	     access(x,++size);
	     //cout<<qjsum(1,1,n,1,n)<<endl;
	 }
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值