题解:P10121 『STA - R4』保险丝

6.2内训模拟赛题。

P10121 『STA - R4』保险丝

显然点越靠近点 u 越容易成为 u 的半邻域,越远离点 u 越不容易成为 u 的半邻域,所以点 u 的半邻域是一个连通块状。

需要一点注意力可以发现度数为 1 或 2 的点作为 v 时无贡献(Fibonacci 数列第一、二项都为 1),称度数 >= 2 的点为分叉点,进一步可以发现一个分叉点往上跳直到跳到上一个分叉点之前(类似于提取下面带分叉上为链状结构)作为 u 的贡献都是一样的,可以一起处理掉。

核心部分的计算流程是,

  • 把树拍成 dfs 序列,子树编号连续
  • 先算出半邻域的点数:\(x\) 的半邻域内点 \(y\) 满足 \(dep_y-dep_x \le dist_y\),因此 \(dep_x \ge dep_y - dist_y\),按 \(dep\) 分层后双指针。
  • 求出每个点的半邻域中所有的分叉点
  • 依次计算 \(f_i\),在线段树上单点加上分叉点的贡献,区间查询贡献积。注意要按照从下到上的顺序加入线段树。

这么做时间正确的原因是,n 个点半领域和上界是 \(O(nlogn)\),证明考虑构造一棵满二叉树,一个节点最多在 \(logn\) 个点的半邻域内。于是这样做就是 \(O(nlog^2n)\) 的,而且这是跑不满的所以可以过 1e6。略微卡常,洛谷上需要评测机波过去。为啥场上最优解是 \(O(n^2)\) 的,谴责赛时水水数据。

点击查看代码
#include <bits/stdc++.h>
#define int long long
#define lc rt * 2
#define rc rt * 2 + 1
#define mid ((l + r) >> 1)
using namespace std;
const int N = 1000010;
const int p = 994007158;
int hed[N],nxt[N * 2],to[N * 2],n,tote,deg[N],totf,d[N],dep[N],fb[N],ans[N],tp[N];
int sta[N],tots,dfn[N],totn,sz[N],b[N],Tr[N],pt[N],tr[N * 4],faa[N];
vector<int> v[N],f[N];
void add(int &x,int y){x += y;if(x >= p)x -= p;}
void addedge(int x,int y){tote++,nxt[tote] = hed[x],to[tote] = y,hed[x] = tote;}
void dfs1(int x,int fa){
	d[x] = 1e9;dep[x] = dep[fa] + 1,faa[x] = fa;
	for(int i = hed[x]; i; i = nxt[i]){
		int y = to[i];if(y == fa)continue;
		dfs1(y,x);d[x] = min(d[x],d[y] + 1);
	}
	if(d[x] == 1e9)d[x] = 0;
}
void dfs2(int x,int fa){
	if(deg[x] > 2 || x == 1)tp[x] = sta[tots],sta[++tots] = x;
	dfn[x] = ++totn;sz[x] = 1;
	for(int i = hed[x]; i; i = nxt[i]){
		int y = to[i];
		if(y == fa)continue;
		d[y] = min(d[y],d[x]);dfs2(y,x);sz[x] += sz[y];
	}
	if(deg[x] > 2 || x == 1)tots--;
}
bool cmp(int x,int y){return dep[x] - d[x] < dep[y] - d[y];}
void addT(int x,int y){x++;for(; x <= n + 1; x += (x & -x))Tr[x] += y;}
int sumT(int x){x++;int res = 0;for(; x; x -= (x & -x)){res += Tr[x];}return res;}
void build(int rt,int l,int r){
	tr[rt] = 1;
	if(l == r){return;}
	build(lc,l,mid);build(rc,mid + 1,r);
}
void modify(int rt,int l,int r,int x,int y){
	if(l == r){tr[rt] = y;return;}
	if(mid >= x)modify(lc,l,mid,x,y);
	else modify(rc,mid + 1,r,x,y);
	tr[rt] = tr[lc] * tr[rc] % p;
}
int query(int rt,int l,int r,int x,int y){
	if(l >= x && r <= y)return tr[rt];
	int res = 1;
	if(mid >= x)res = res * query(lc,l,mid,x,y) % p;
	if(mid + 1 <= y)res = res * query(rc,mid + 1,r,x,y) % p;
	return res;
}
signed main(){
	//freopen("fuse.in","r",stdin);
	//freopen("fuse.out","w",stdout);
	scanf("%lld",&n);
	fb[1] = fb[2] = 1;
	for(int i = 3; i <= n; i++)fb[i] = (fb[i - 1] + fb[i - 2]) % p;
	for(int i = 2,x; i <= n; i++)scanf("%lld",&x),addedge(i,x),addedge(x,i),deg[i]++,deg[x]++;
	dfs1(1,0);dfs2(1,0);
	
	for(int i = 1; i <= n; i++)b[i] = i,v[dep[i]].push_back(i);
	sort(b + 1,b + n + 1,cmp);
	for(int i = 1,j = 0; i <= n; i++){
		while(j < n && dep[b[j + 1]] - i <= d[b[j + 1]])addT(dfn[b[++j]],1);
		for(int j = 0; j < v[i].size(); j++){
			pt[v[i][j]] = sumT(dfn[v[i][j]] + sz[v[i][j]] - 1) - sumT(dfn[v[i][j]] - 1);
		}
	}
	for(int i = n; i >= 1; i--){
		int x = b[i];
		if(x != 1 && deg[x] <= 2)continue;
		for(int j = x; j && dep[x] - dep[j] <= d[x]; j = faa[j])f[j].push_back(x);
	}
	build(1,1,n);
	for(int i = 1; i <= n; i++){
		for(int j = 0; j < f[i].size(); j++){
			int x = f[i][j];
			modify(1,1,n,dfn[x],fb[deg[x]]);
			add(ans[i],query(1,1,n,dfn[x],dfn[x] + sz[x] - 1) * (dep[x] - max(dep[tp[x]],dep[i] - 1)) % p);
			pt[i] -= dep[x] - max(dep[tp[x]],dep[i] - 1);
		}
		add(ans[i],pt[i]);
		for(int j = 0; j < f[i].size(); j++)modify(1,1,n,dfn[f[i][j]],0);
	}
	
	int Ans = 0;
	for(int i = 1; i <= n; i++)Ans ^= ans[i];
	printf("%lld",Ans);
	return 0;
}

posted @ 2026-06-03 11:00  Jenny_yu  阅读(8)  评论(0)    收藏  举报