首页 > 其他分享 >[THUWC2018]城市规划的题解

[THUWC2018]城市规划的题解

时间:2024-03-27 21:45:26浏览次数:18  
标签:城市规划 tu int 题解 ll THUWC2018 tree tv root

[THUWC2018]城市规划

连通块问题,我们考虑树形 DP。

设 \(f_{u,j}\) 表示在以 \(u\) 为根的子树内,选的颜色集合为 \(a_{u},j\)(两个颜色都必须选)且必须选点 \(u\) 的情况下的连通块个数。

特殊的,\(f_{u,a_{u}}\) 表示颜色只有 \(a_{u}\) 的情况数。

对于 \(v\in son_u\),有:

若 \(a_{u}=a_{v}\):

对于每一个颜色 \(i\),有:

\[f_{u,i}=f_{v,i}\times f_{u,a_{u}}+f_{v,a_{v}}\times f_{u,i}+f_{u,i}\times f_{v,i}+f_{u,i} \]

特别的,有:

\[f_{u,a_{u}}=f_{u,a_{u}}\times (f_{v,a_{v}}+1) \]

否则:

\[f_{u,a_{v}}=f_{u,a_{v}}\times (f_{v,a_{v}}+f_{v,a_{u}}+1)+f_{u,a_{u}}\times (f_{v,a_{v}}+f_{v,a_{u}}) \]


但是,这样子时间复杂度太大,接受不了。

我们发现上面对于的操作分别为线段树合并和线段树单点查询、修改,最后的减去对应区间修改,以此优化即可。

注意代码细节。

代码:

#include<bits/stdc++.h>
#define ll long long
using namespace std;

const int N=1e5+5;
const ll mod=998244353;
int n,a[N];
struct node{
	int l,r;
	ll sum,mul;
}tree[N<<6];
int root[N],tot;
void clean(int k){
	tree[k].sum%=mod,tree[k].mul%=mod;
}
int build(){
	int k=++tot;
	tree[k].mul=1;
	return k;
}
void push_down(int k){
	if(tree[k].mul==1) return;
	if(tree[k].l){
		tree[tree[k].l].mul*=tree[k].mul;
		tree[tree[k].l].sum*=tree[k].mul;
		clean(tree[k].l);
	}
	if(tree[k].r){
		tree[tree[k].r].mul*=tree[k].mul;
		tree[tree[k].r].sum*=tree[k].mul;
		clean(tree[k].r);
	}
	tree[k].mul=1;
}
void push_up(int k){
	tree[k].sum=tree[tree[k].l].sum+tree[tree[k].r].sum;
	clean(k);
}
ll query(int k,int l,int r,int pos){
	if(!k) return 0;
	if(l==r) return tree[k].sum%mod;
	push_down(k);
	int mid=(l+r)>>1;
	if(pos<=mid) return query(tree[k].l,l,mid,pos);
	else return query(tree[k].r,mid+1,r,pos);
}

int insert(int k,int l,int r,int pos){
	if(!k) k=build();
	if(l==r){
		tree[k].sum=1;
		return k;
	}
	push_down(k);
	int mid=(l+r)>>1;
	if(pos<=mid) tree[k].l=insert(tree[k].l,l,mid,pos);
	else tree[k].r=insert(tree[k].r,mid+1,r,pos);
	push_up(k);
	return k;
}
int merge(int x,int y,int l,int r,ll tu,ll tv,int cu){
	if(!x&&!y) return 0;
	if(!x){//f[u][l-r]都没有 
		tree[y].sum*=tu;
		tree[y].mul*=tu;
		clean(y);
		return y;
	}
	if(!y){
		tree[x].sum*=(tv+1ll);
		tree[x].mul*=(tv+1ll);
		clean(x);
		return x;
	}
	if(l==r){
		if(l==cu) tree[x].sum=tree[x].sum*(tv+1)%mod;
		else tree[x].sum=tree[y].sum*tu%mod+tree[x].sum*tv%mod+tree[x].sum*tree[y].sum%mod+tree[x].sum;
		tree[x].sum%=mod;
		return x;
	}
	int mid=(l+r)>>1;
	push_down(x),push_down(y);
	tree[x].l=merge(tree[x].l,tree[y].l,l,mid,tu,tv,cu);
	tree[x].r=merge(tree[x].r,tree[y].r,mid+1,r,tu,tv,cu);
	push_up(x);
	return x;
}
int mul(int k,int l,int r,int pos,ll tu,ll tv,ll fv_cu){
	if(!k) k=build();
	if(l==r){
		tree[k].sum=tree[k].sum*(fv_cu+tv+1)%mod+tu*(fv_cu+tv)%mod;
		tree[k].sum%=mod;
		return k;
	}
	push_down(k);
	int mid=(l+r)>>1;
	if(pos<=mid) tree[k].l=mul(tree[k].l,l,mid,pos,tu,tv,fv_cu);
	else tree[k].r=mul(tree[k].r,mid+1,r,pos,tu,tv,fv_cu);
	push_up(k);
	return k;
}
ll ans=0;
vector<int> G[N];
void dfs(int u,int fa){
	root[u]=insert(root[u],1,n,a[u]);
	ll tu,tv,fv_cu;
	for(auto v : G[u]){
		if(v==fa) continue;
		dfs(v,u);
		tu=query(root[u],1,n,a[u]);tv=query(root[v],1,n,a[v]);
		if(a[v]!=a[u]){
			fv_cu=query(root[v],1,n,a[u]);
			root[u]=mul(root[u],1,n,a[v],tu,tv,fv_cu);
		}
		else{
			root[u]=merge(root[u],root[v],1,n,tu,tv,a[u]);
		}
	}
	ll sum=tree[root[u]].sum;
	ans=(ans+sum)%mod;
}

int main(){
	scanf("%d",&n);
	for(int i=1;i<=n;i++)scanf("%d",&a[i]);
	int x,y;
	for(int i=1;i<n;i++){
		scanf("%d%d",&x,&y);
		G[x].push_back(y);
		G[y].push_back(x);
	}
	dfs(1,0);
	printf("%lld\n",ans);
	return 0;
}

标签:城市规划,tu,int,题解,ll,THUWC2018,tree,tv,root
From: https://www.cnblogs.com/123456xwd/p/18100316

相关文章

  • 【题解】P10235 [yLCPC2024] C. 舞萌基本练习
    P10235舞萌基本练习题解思路看到最大值最小首先考虑二分答案。由于答案满足单调性,可以二分不优美度的最大值,也就是逆序对数的最大值。我们在每次增加一个元素的时候都要求解当前区间的逆序对数,所以不能用归并排序求逆序对数,考虑树状数组解法。如果不会树状数组求逆序对,请出......
  • 【题解】P4553 80人环游世界
    一眼丁真,鉴定为费用流思路类似于路径覆盖问题。考虑把每个点拆成入点\(x\)和出点\(y\)。对于每个点的入点\(x\)都向这个点的出点\(y\)连一条容量为\(V_i\),费用为\(0\)的边来控制每个点会被访问\(V_i\)次。然后建一个中间点\(p\),连一条\(s\Rightarrowp\)容量......
  • 2023第14届蓝桥杯大赛软件赛省赛C/C++大学A组第6题题解
    目录问题描述:方法一:dfs暴力模拟(45%)方法二:dfs剪枝(100%)问题描述:        小蓝正在一个瓜摊上买瓜。瓜摊上共有n个瓜,每个瓜的重量为Ai。小蓝刀功了得,他可以把任何瓜劈成完全等重的两份,不过每个瓜只能劈一刀。小蓝希望买到的瓜的重量的和恰好为m。请问小蓝至......
  • P7137 [THUPC2021 初赛] 切切糕 题解
    题目传送门前置知识博弈论解法由于本题是CF1628D1GameonSum(EasyVersion)的扩展,故先从CF1628D1GameonSum(EasyVersion)讲解。CF1628D1GameonSum(EasyVersion)设\(x_{i}\)表示第\(i\)轮时Alice选择的数。设\(f_{i,j}\)表示已经进行了\(i\)轮,且......
  • Spring Boot 工程开发常见问题解决方案,日常开发全覆盖
    本文是SpringBoot开发的干货集中营,涵盖了日常开发中遇到的诸多问题,通篇着重讲解如何快速解决问题,部分重点问题会讲解原理,以及为什么要这样做。便于大家快速处理实践中经常遇到的小问题,既方便自己也方便他人,老鸟和新手皆适合,值得收藏......
  • 【赛题解析】【网络建设与运维】第三阶段Linux Vsftpd部分答案解析
    培训、环境、资料、考证公众号:波比网络公众号2:波比网络工作室网络建设与运维群:685678820波比网络专注于技能提升,赋能ftp服务任务描述:请采用ftp服务器,实现文件安全传输。1.配置 linux1为ftp服务器,安装vsftpd,新建本地用户xiaoming,本地用户登陆ftp后的目录为/var/ft......
  • CF1879的题解
    (一)对于第一个问题,直接搜出字符串中有多少个仅由\(0\)或\(1\)组成的串组成的。对于第二个问题,每个串只有一个能选,然后选择顺序有所不同,具体看代码。(二)AC代码。#defineintlonglong#definemd998244353usingnamespacestd;intt,s[200010];charch[200010];sign......
  • CF340B的题解
    (一)枚举对角线。然后分别找正在对角线上方的点与对角线端点构成三角形面积的最大值。和在对角线下方的点与对角线端点构成三角形面积的最大值。如果所有点都在同侧,那么不算。通过过两点直线的解析式求出另一点在直线的哪一侧。(二)AC代码。#include<bits/stdc++.h>#define......
  • CF1864C的题解
    (一)可以将\(x\)转为二进制。考虑一个数的二进制\((1\dots10\dots0)\)。其中,第一个省略号中有什么不确定,第二个省略号里都是\(0\)。易得,每个数都可以看成这种形式。那么可以每次去掉最后一位的\(1\),易证减去的数是原数的因数。最后会得到形如\((10\dots0)\),省略号中全是......
  • AT_abc345_c的题解
    (一)首先交换相同字符不改变字符串形态,那么就先统计是否有相同字符。交换不同字符容易证明不同操作后字符串各不相同。用前缀和或后缀和维护\(i+1\)到\(n\)中与\(i\)位置字符不同的数量。(二)AC代码。#include<bits/stdc++.h>#defineintlonglongusingnamespacestd......