【校内互侧】Sunshine’s city (lct+线段树)

33 篇文章 0 订阅
18 篇文章 0 订阅
Sunshine’s city(city.cpp)
【问题描述】
    在很久很久之前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

——————————————————————————————————

【题解】【lct+线段树】

【可以发现,这道题中的修改操作很像lct中的access操作,so,必然lct啊,但是这个题的lct题比较好写,只有link和access操作;然后因为要求区间和,所以要用线段树来维护,区间的话用dfs序搞一下就行了(求dfs序的时候要用非递归,我因为用了递归,RERERE……)】

#include<cstdio>
#include<cstring>
#include<algorithm>
#define ll long long
using namespace std;
int f[400010],ch[400010][2],col[400010],cnt;
int a[400010],nxt[400010],p[200010],tot;
int cur[400010],L[400010],R[400010],pre[400010];
int que[400010],top;
ll sum[1600010],delta[1600010];
int n,m;
inline void add(int x,int y)
{
	tot++; a[tot]=y; nxt[tot]=p[x]; p[x]=tot;
	tot++; a[tot]=x; nxt[tot]=p[y]; p[y]=tot;
}
inline void pushdown(int now,int l,int r,int mid)
{
	if(delta[now])
	 {
	 	sum[(now<<1)]+=(mid-l+1)*delta[now]; sum[(now<<1)|1]+=(r-mid)*delta[now];
	 	delta[(now<<1)]+=delta[now]; delta[(now<<1)|1]+=delta[now];
	 	delta[now]=0;
	 }
}
inline void updata(int now)
{
	sum[now]=sum[(now<<1)]+sum[(now<<1)|1];
}
void change(int now,int l,int r,int al,int ar,int v)
{
	if(al<=l&&r<=ar)
	 {
	 	sum[now]+=(ll)(r-l+1)*(ll)v;
	 	delta[now]+=(ll)v;
	 	return;
	 }
	int mid=(l+r)>>1;
	pushdown(now,l,r,mid);
	if(al<=mid) change((now<<1),l,mid,al,ar,v);
	if(ar>mid) change((now<<1)|1,mid+1,r,al,ar,v);
	updata(now);
}
ll ask(int now,int l,int r,int al,int ar)
{
	if(al<=l&&r<=ar) return sum[now];
	int mid=(l+r)>>1;
	pushdown(now,l,r,mid);
	ll ans=0;
	if(al<=mid) ans+=ask((now<<1),l,mid,al,ar);
	if(ar>mid) ans+=ask((now<<1)|1,mid+1,r,al,ar);
	return ans;
}

void dfs()
{
	top=0;
	for(int i=1;i<=n;++i) cur[i]=p[i];
    que[++top]=1; pre[1]=0;
	while(top)
	 {
	 	int x=que[top];
	 	if(a[cur[x]]==pre[x]) cur[x]=nxt[cur[x]];
	 	if(!cur[x])
	 	 {
	 	 	--top;
	 	 	R[x]=cnt;
	 	 	continue;
		  }
		int v=a[cur[x]];
		que[++top]=v; pre[v]=x; L[v]=++cnt;
		cur[x]=nxt[cur[x]];
	}	
}

inline int isroot(int x)
{
	return (ch[f[x]][0]!=x&&ch[f[x]][1]!=x);
}
inline int get(int x)
{
	return ch[f[x]][1]==x;
}
inline void rotate(int x)
{
	int fa,olf,which;
	fa=f[x]; olf=f[fa]; which=get(x);
	if(!isroot(fa)) ch[olf][ch[olf][1]==fa]=x;
	ch[fa][which]=ch[x][which^1]; f[ch[x][which^1]]=fa;
	f[fa]=x; ch[x][which^1]=fa; f[x]=olf;
}
inline void splay(int x)
{
	while(!isroot(x))
	 {
	 	int y=f[x];
	 	if(!isroot(y))
	 	 rotate(get(x)==get(y)?y:x);
	 	rotate(x);
	 }
}
inline void link(int x,int y)
{
	f[x]=y;
}
inline int find(int x)
{
	while(ch[x][0]) x=ch[x][0];
	return x;
}
inline void access(int x,int num)
{
	int tmp=0;
	while(x)
	 {
	 	col[x]=num;
	 	splay(x);
	 	int t=find(ch[x][1]);
	 	if(t) 
		   change(1,1,n,L[t],R[t],1);
	 	ch[x][1]=tmp;
	 	int t1=find(tmp);
	 	if(t1) 
		   change(1,1,n,L[t1],R[t1],-1);
	 	tmp=x; x=f[x];
	 }
}

int main()
{
	freopen("city.in","r",stdin);
	freopen("city.out","w",stdout);
	int i; tot=0;
	memset(p,0,sizeof(p));
	memset(nxt,0,sizeof(nxt));
	scanf("%d",&n);
	for(i=1;i<n;++i)
	 {
	 	int x,y;
	 	scanf("%d%d",&x,&y);
	 	x++; y++;
	 	add(x,y); link(y,x);
	 }
	dfs();
	for(i=2;i<=n;++i) change(1,1,n,L[i],R[i],1);
	scanf("%d\n",&m);
	int size=n;
	for(i=1;i<=n;++i) col[i]=i;
	for(i=1;i<=m;++i)
	 {
	 	int x; char opt;
	 	scanf("%c%d",&opt,&x);
	 	getchar();
	 	x++;
	 	if(opt=='q') printf("%lld\n",ask(1,1,n,L[x],R[x]));
	 	 else access(x,++size);
	 }
	return 0;
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值