NOIP2023模拟9联测30 T4 金牌

LCA 还能 \(O(1)\)……

思路

思路非常简单,可考试就是想歪成统计指数了……

将一条穿过 \((x,y)\) 的路径 \((u,v)\) 分为 \(u \to x \to y \to v\),所以说对答案的贡献为:

\[2^{dis(u,x)+dis(x,y)+dis(y,v)}=2^{dis(u,x)}\times 2^{dis(x,y)}\times 2^{dis(y,v)}
\]

如果把 \((x,y)\) 的路径上的边断开,形成了若干联通块,设 \(x\) 所在的联通块为 \(E_x\),\(y\) 所在的联通块为 \(E_y\),答案为:

\[2^{dis(x,y)} {\sum_{i\in E_x} 2^{dis(i,x)}} \sum_{i=E_y} 2^{dis(y,i)}
\]

有了这个式子可以可以分类讨论求答案。

先以 1 为根建树。

若 u,v 的 lca 不是 u,v 中一点

可以通过 \(sum_u=2\times\sum\limits_{v\in u.sons} sum_v\),预处理出子树 \(u\) 内到 \(u\) 的价值,\(y\) 同理。

又可以通过 \(dep_u+dep_v-2\times dep_{lca(u,v)}\) 求出 \(u\) 和 \(v\) 两点间的距离。(\(dep\) 是以 1 为根时的深度)

代入公式即可求值。

若 u,v 的 lca 是 u,v 中一点

这样就比较复杂了,画一张图:

发现当 \(u\) 为根时,答案就是 \(v\) 子树内的距离和乘上距离的贡献再乘 树\(u\)的距离和 减去 \((u,v)\) 路径上 \(u\) 的儿子子树内的距离和。

格式化就是 \((sum_u-sum_{u.son})\times 2^{dis(u,v)}\times sum_v\)。

\(u\) 做为根时 \(u\) 的 \(sum\) 可以换根 dp 快速求,\(sum_v\) 和 \(sum_{u.son}\) 可以直接用以 1 为根时求的 \(sum\),距离可以用上述讨论的式子求。

\(u.son\) 可以倍增时较深的节点先跳到较浅节点深度 \(-1\) 的位置,查看如果此时较深节点的父亲是较浅的节点,就可以判断为这种情况,并且使 \(u.son\) 等于当前较深节点。

CODE

文中使用倍增求 lca。

#include<bits/stdc++.h>
using namespace std; #define ll long long #define mod 998244353 #define S second #define F first const int maxn=2e6+5; struct node
{
int to,nxt;
}edge[maxn*2]; int n,tot;
int head[maxn],f[maxn][25],deep[maxn]; ll ans[maxn],sum[maxn]; vector< pair<int,int> >E[maxn]; ll ksm(ll x,ll y)
{
ll sum=1;
for(;y;y/=2,x=x*x%mod) if(y&1) sum=sum*x%mod;
return sum;
} void add(int x,int y)
{
tot++;
edge[tot].to=y;
edge[tot].nxt=head[x];
head[x]=tot;
} void dfs(int u)
{
sum[u]=1;
for(int i=head[u];i;i=edge[i].nxt)
{
int v=edge[i].to;
if(v==f[u][0]) continue;
deep[v]=deep[u]+1;
f[v][0]=u;
for(int j=1;j<=20;j++) f[v][j]=f[f[v][j-1]][j-1];
dfs(v);
sum[u]=(sum[u]+sum[v]*2%mod)%mod;
}
}
pair<int,int> Lca(int x,int y)
{
if(deep[x]<deep[y]) swap(x,y);
for(int i=20;i>=0;i--) if(deep[f[x][i]]>deep[y]) x=f[x][i];
if(f[x][0]==y) return make_pair(f[x][0],x);//情况 2,返回 lca 和 u.son
if(deep[x]>deep[y]) x=f[x][0];
for(int i=20;i>=0;i--) if(f[x][i]!=f[y][i]) x=f[x][i],y=f[y][i];
return make_pair(f[x][0],0);
} void dfs_hg(int u,ll dis)//换根 dp
{
if(u!=1) dis=((dis-sum[u]*2%mod+mod)%mod*2%mod+sum[u])%mod;
for(pair<int,int> v:E[u]) ans[v.S]=(ans[v.S]*((dis-sum[v.F]*2+mod)%mod))%mod;
for(int i=head[u];i;i=edge[i].nxt)
{
int v=edge[i].to;
if(v==f[u][0]) continue;
dfs_hg(v,dis);
}
} int main()
{
scanf("%d",&n);
for(int i=1;i<n;i++)
{
int x,y;
scanf("%d%d",&x,&y);
add(x,y);
add(y,x);
} deep[1]=1;
dfs(1); int m;
scanf("%d",&m);
for(int i=1;i<=m;i++)
{
int x,y;
scanf("%d%d",&x,&y);
pair<int,int> lca=Lca(x,y);//first 是 lca,seoncd 是 u.son
if(lca.S)
{
E[lca.F].push_back(make_pair(lca.S,i));
int k=x;
if(lca.F==x) k=y;
ans[i]=sum[k]*ksm(2,deep[x]+deep[y]-2*deep[lca.F])%mod;
}
else ans[i]=sum[x]*ksm(2,deep[x]+deep[y]-2*deep[lca.F])%mod*sum[y]%mod;
} dfs_hg(1,sum[1]); for(int i=1;i<=m;i++) printf("%lld\n",(ans[i]+mod)%mod);
}

结束了吗?

尽管倍增和树剖的时间复杂度来到了优秀的 \(O(\log n)\) 但这题卡常,我们不得不用 \(O(1)\) 的预处理 lca。

啊这.jpg

lca 部分见博客 预处理 O(1) 求 lca。(后面填)

在求 lca 的过程中,我们可以使用手写栈存下这一个节点的所有祖先,后面遍历虚边时,如果连接的点在栈中,那么 u.son 就等于栈中连接的点的下一个位置。

CODE

#include<bits/stdc++.h>
using namespace std; #define ll long long
#define mod 998244353
#define S second
#define F first
#define ri register int const int maxn=1e6+5; struct node
{
int to,nxt;
}edge[maxn*2]; int n,tot,tp;
int head[maxn],deep[maxn],x[maxn],y[maxn],vis[maxn],stk[maxn],fa[maxn]; ll ans[maxn],sum[maxn]; pair<int,int> Lca[maxn]; vector< pair<int,int> >E[maxn],EL[maxn]; inline int read() {
int s = 0, w = 1;
char ch = getchar();
while (ch < '0' || ch > '9') {
if (ch == '-')
w = -1;
ch = getchar();
}
while (ch >= '0' && ch <= '9') s = s * 10 + ch - '0', ch = getchar();
return s * w;
}
inline void write(ll X)
{
if(X<0) {X=~(X-1); putchar('-');}
if(X>9) write(X/10);
putchar(X%10+'0');
} inline ll ksm(ll x,ll y)
{
ll sum=1;
for(;y;y/=2,x=x*x%mod) if(y&1) sum=sum*x%mod;
return sum;
} inline void add(int x,int y)
{
tot++;
edge[tot].to=y;
edge[tot].nxt=head[x];
head[x]=tot;
} inline int frt(int u)
{
if(fa[u]==u) return u;
return fa[u]=frt(fa[u]);
}
inline void dfs(int u)//O(1) lca
{
stk[++tp]=u;
vis[u]=tp;//u 节点在栈中的位置
sum[u]=1;
for(ri i=head[u];i;i=edge[i].nxt)
{
int v=edge[i].to;
if(vis[v]) continue;
deep[v]=deep[u]+1;
dfs(v);
fa[v]=u;
sum[u]=(sum[u]+sum[v]*2%mod)%mod;
}
for(pair<int,int> i:EL[u])
{
if(vis[i.F]!=-1&&vis[i.F]!=0) Lca[i.S].S=stk[vis[i.F]+1];//如果栈,但没出栈,u.son 赋值
else if(vis[i.F]) Lca[i.S].F=frt(i.F);
}
tp--;
vis[u]=-1;//-1 表示入过栈,但已出栈
} inline void dfs_hg(int u,ll dis,int f)
{
if(u!=1) dis=((dis-sum[u]*2%mod+mod)%mod*2%mod+sum[u])%mod;
for(pair<int,int> v:E[u]) ans[v.S]=(ans[v.S]*((dis-sum[v.F]*2+mod)%mod))%mod;
for(ri i=head[u];i;i=edge[i].nxt)
{
int v=edge[i].to;
if(v==f) continue;
dfs_hg(v,dis,u);
}
} int main()
{
n=read();
for(ri i=1;i<n;i++)
{
int x,y;
x=read(),y=read();
add(x,y);
add(y,x);
} int m;
m=read();
for(ri i=1;i<=m;i++)
{
x[i]=read(),y[i]=read();
EL[x[i]].push_back(make_pair(y[i],i)),EL[y[i]].push_back(make_pair(x[i],i));//虚边
} for(ri i=1;i<=n;i++) fa[i]=i;
dfs(1); for(ri i=1;i<=m;i++)
{
pair<int,int> lca=Lca[i];
if(lca.S)
{
E[lca.F].push_back(make_pair(lca.S,i));
int k=x[i];
if(lca.F==x[i]) k=y[i];
ans[i]=sum[k]*ksm(2,deep[x[i]]+deep[y[i]]-2*deep[lca.F])%mod;
}
else ans[i]=sum[x[i]]*ksm(2,deep[x[i]]+deep[y[i]]-2*deep[lca.F])%mod*sum[y[i]]%mod;
} dfs_hg(1,sum[1],0); for(ri i=1;i<=m;i++) write((ans[i]+mod)%mod),putchar('\n');
}

NOIP2023模拟9联测30 T4 金牌的更多相关文章

  1. [NOI.AC省选模拟赛3.30] Mas的童年 [二进制乱搞]

    题面 传送门 思路 这题其实蛮好想的......就是我考试的时候zz了,一直没有想到标记过的可以不再标记,总复杂度是$O(n)$ 首先我们求个前缀和,那么$ans_i=max(pre[j]+pre[i ...

  2. NOIP2017赛前模拟10月30日总结

    题目1: n个人参赛(n<=100000),每个人有一个权值··已知两个人权值绝对值之差小于等于K时,两个人都有可能赢,若大于则权值大的人赢···比赛为淘汰制,进行n-1轮·问最后可能赢的人有多 ...

  3. [jzoj 6093] [GDOI2019模拟2019.3.30] 星辰大海 解题报告 (半平面交)

    题目链接: https://jzoj.net/senior/#contest/show/2686/2 题目: 题解: 说实话这题调试差不多花了我十小时,不过总算借着这道题大概了解了计算几何的基础知识 ...

  4. [jzoj 6092] [GDOI2019模拟2019.3.30] 附耳而至 解题报告 (平面图转对偶图+最小割)

    题目链接: https://jzoj.net/senior/#main/show/6092 题目: 知识点--平面图转对偶图 在求最小割的时候,我们可以把平面图转为对偶图,用最短路来求最小割,这样会比 ...

  5. @NOI模拟2017.06.30 - T3@ Right

    目录 @description@ @solution@ @part - 1@ @part - 2@ @accepted code@ @details@ @description@ JOHNKRAM 和 ...

  6. @NOI模拟2017.06.30 - T1@ Left

    目录 @description@ @solution@ @accepted code@ @details@ @description@ JOHNKRAM 最近在研究排序网络,但他发现他不会制作比较器, ...

  7. Python实现网站模拟登陆

    一.实验简介 1.1 基本介绍 本实验中我们将通过分析登陆流程并使用 Python 实现模拟登陆到一个实验提供的网站,在实验过程中将学习并实践 Python 的网络编程,Python 实现模拟登陆的方 ...

  8. [7.18NOIP模拟测试5]砍树 题解(数论分块)

    题面(加密) 又考没学的姿势……不带这么玩的…… 考场上打了个模拟 骗到30分滚粗了 稍加思考(滑稽)可将题面转化为: 求一个最大的$d$,使得 $\sum \limits _{i=1}^n {(\l ...

  9. NOIP 模拟 七十七

    100+60+95+30; T4 一个变量打错挂了40.. T1 最大或 考虑从高到低枚举的二进制位,然后和的对应二进制位进行比较.如果两 者相同,那么不论怎么选择,,答案在这个位置上的值一定和在这个 ...

  10. matlab演奏最炫民族风的代码注释

    用Matlab来放音乐,和用单片机加蜂鸣器放音乐的原理都差不多,就是把连续的声音信号事先转换成用数字信号,然后用扬声器按照一定的节奏放出来.换句话说,演唱者是把声音经过麦克风转换成电信号,录音设备对这 ...

随机推荐

  1. hass安装tileboard详细

    首先下载tileboard https://github.com/resoai/TileBoard/releases/download/v2.10.2/TileBoard.zip 下载之后前往hass ...

  2. 防止npm被墙的小技巧

    方法一: 全局安装中国服务器的包管理工具 npm i cnpm --global 下载包的时候用cnpm取代npm 方法二: 在小黑板输入:npm config set registry https: ...

  3. 【Git代码仓库】之合并分支代码操作到主干代码上(界面版/命令版)

    一.代码管理仓库,合并分支代码到主干(界面版*) 1.从远程Git代码仓库克隆到本地 # Git克隆 git clone git@e.coding.net:XXX/SQM/SC_WEB_Project ...

  4. android 访问域名接口报错

    1. 移动端访问https域名及接口,显示 java.net.UnknownHostException: Unable to resolve host "xxx" : No add ...

  5. 设线性表中每个元素有两个数据项k1和k2,现对线性表按一下规则进行排序:先看数据项k1,k1值小的元素在前,大的在后;在k1值相同的情况下,再看k2,k2值小的在前,大的在后。满足这种要求的

    题目: 设线性表中每个元素有两个数据项k1和k2,现对线性表按一下规则进行排序:先看数据项k1,k1值小的元素在前,大的在后:在k1值相同的情况下,再看k2,k2值小的在前,大的在后.满足这种要求的排 ...

  6. 单 log 实现 区间加减,查询区间 gcd

    主要是查询,要将 log 个区间拿出来依次求 gcd,当然如果是 O(1) gcd 的话可以直接求就是了.

  7. fluent python-chap2

    1. 内置序列类型 容器序列: list tuple collections.deque 可以存放不同类型的数据. 存放的是它们所包含的任意类型的对象的引用. 扁平序列: str bytes byte ...

  8. MVC @Html.TextBox 属性

    MVC中设置文本框不可修改(@Html.TextBox) mvc前台: @Html.TextBox("id","name", new {@Readonly = ...

  9. MySQL 大表拆分

    概述 在实际工作中,在关系数据库(MySQL.PostgreSQL)的单表数据量上亿后,往往会出现查询和分析变慢甚至无法执行统计分析的情况.这时就需要将大表拆分为多个小表,将小表分布在多个数据库上,形 ...

  10. JAVAEE——JDK安装

    1.JDK下载 (1)jdk官网 1.输入下载地址:Oracle Software Downloads | Oracle 2.向下滑动,找到Developer Downloads后点击java 3.点 ...