题解: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;
}

浙公网安备 33010602011771号