2024 0130 模拟赛


做法:
- \(n,q\) 范围均在 \(2000\) 以内,允许我们每一个询问都进行一次 \(dfs\) ,将 \(u\) 到 \(v\) 之间的这条链拿出来,这样就转化成了一个链(一个序列)上的问题了。
- o——o——o——o——o
这是一条链- 我们设最左端的点为 \(i\),最右端的点为 \(j\),其中的点为 \(i+1\),\(j-1\) 等等。
- 观察题目中给的 \(dis(i,j)\) 的定义式,我们可以知道就是从 \(i~j\) 的点集中任意选取两个点,并且 \((i,j)\) 反过来的 \((j,i)\) 只算一遍。因此,我们可以得出这样一个等价于原式子的式子:
\[\operatorname{dis}(i,j)=\sum\limits_{l=i}^j\sum\limits_{r=l+1}^j\operatorname{dis}(l,r)-\operatorname{dis}(i,j)
\]
- (右边减去一个 \(\operatorname{dis}(i,j)\) 是因为枚举 \(l,r\) 时算到了)
- 这样,有式子①:
\[2\operatorname{dis}(i,j)=\sum\limits_{l=i}^j\sum\limits_{r=l+1}^j\operatorname{dis}(l,r)
\]
- 所以推:
\[\begin{aligned}
2\operatorname{dis}(i,j)&=\sum\limits_{l=i}^j\sum\limits_{r=l+1}^j\operatorname{dis}(l,r)\\
&=\sum\limits_{l=i}^j\left(\sum\limits_{l=i}^{j-1}\operatorname{dis}(l,r)+\operatorname{dis}(l,j)\right)\\
&=\sum\limits_{l=i}^j\sum\limits_{r=l+1}^{j-1}\operatorname{dis}(l,r)+\sum\limits_{l=i}^j\operatorname{dis}(l,j)\\
&=2\operatorname{dis}(i,j-1)+\operatorname{dis}(i,j)+\sum\limits_{l=i+1}^j\operatorname{dis}(l,j)\\
&=2\operatorname{dis}(i,j-1)+\operatorname{dis}(i,j)+\sum\limits_{l=i+1}^j\sum\limits_{r=l+1}^j\operatorname{dis}(l,r)-\sum\limits_{l=i+1}^j\sum\limits_{r=l+1}^{j-1}\operatorname{dis}(l,r)\\
&=2\operatorname{dis}(i,j-1)+\operatorname{dis}(i,j)+2\operatorname{dis}(i+1,j)-2\operatorname{dis}(i+1,j-1)
\end{aligned}
\]
-
(其中式子转换用到了前面的式子①)
-
所以:
\[\operatorname{dis}(i,j)=2\operatorname{dis}(i,j-1)+2\operatorname{dis}(i+1,j)-2\operatorname{dis}(i+1,j-1)
\]
- 这样我们就得出了可以 \(O(1)\) 时间递推出来的 \(dis\) 值,所以我们考虑在询问时拿出来一条链的时候,直接递归地搜索这个式子。加上记忆化即可保证时间复杂度 \(O(n^2+nq)\)。
- 注意在使用式子①时,\(i,j\) 不能是相邻的点,所以在算 \(dis\) 时候的特判就是为了解决这个问题。可以看代码。
点击查看代码
#include<bits/stdc++.h>
using namespace std;
namespace Light_Tea{void main();}
signed main(){
Light_Tea::main();
return 0;
}
namespace Light_Tea{
void file(){}
#define int64 long long
const int N=2005;
const int64 MOD=1e9+7;
int n,q;
struct Edge{
int v,w,nxt;
}e[N*2];
int head[N],cnt_e=0;
void add_edge(int u,int v,int w){e[++cnt_e]=(Edge){v,w,head[u]}, head[u]=cnt_e;}
int fa[N],fro[N],a[N];
int64 dis[N][N]; bool vis[N][N];
void dfs(int u,int _fa,int _fro){
fa[u]=_fa; fro[u]=_fro;
for(int i=head[u], v,w; v=e[i].v, w=e[i].w, i; i=e[i].nxt){
if(v==_fa) continue;
dfs(v,u,w);
}
}
int64 query(int l,int r){
if(vis[a[l]][a[r]]) return dis[a[l]][a[r]];
vis[a[l]][a[r]]=vis[a[r]][a[l]]=1;
if(r-l==2){
dis[a[l]][a[r]]=dis[a[r]][a[l]]=(dis[a[l]][a[r-1]]+dis[a[l+1]][a[r]])%MOD;
}else{
int64 ans=0;
if(!vis[a[l+1]][a[r]]) ans=(ans+(query(l+1,r)*2)%MOD)%MOD;
else ans=(ans+(dis[a[l+1]][a[r]]*2)%MOD)%MOD;
if(!vis[a[l]][a[r-1]]) ans=(ans+(query(l,r-1)*2)%MOD)%MOD;
else ans=(ans+(dis[a[l]][a[r-1]]*2)%MOD)%MOD;
if(r-l==3){
ans=(ans-dis[a[l+1]][a[r-1]])%MOD;
}else{
if(!vis[a[l+1]][a[r-1]]) ans=(ans-(query(l+1,r-1)*2)%MOD)%MOD;
else ans=(ans-(dis[a[l+1]][a[r-1]]*2)%MOD)%MOD;
}
dis[a[l]][a[r]]=dis[a[r]][a[l]]=(ans%MOD+MOD)%MOD;
}
return dis[a[l]][a[r]];
}
void main(){
// freopen("c.in","r",stdin);
// freopen("c.out","w",stdout);
scanf("%d",&n);
for(int i=1;i<=n-1;i++){
int u,v,w; scanf("%d%d%d",&u,&v,&w);
add_edge(u,v,w), add_edge(v,u,w);
dis[u][v]=dis[v][u]=w; vis[u][v]=vis[v][u]=1;
}
for(int i=1;i<=n;i++) vis[i][i]=1;
scanf("%d",&q);
while(q--){
int u,v; scanf("%d%d",&u,&v);
dfs(u,0,0);
int cnt=0;
for(int now=v; now!=fa[u]; now=fa[now]) a[++cnt]=now;
int64 ans=query(1,cnt);
printf("%lld\n",ans);
}
// int cnt=0;
// for(int i=1;i<=n;i++){
// for(int j=1;j<=n;j++) cnt+=vis[i][j];
// }
// printf("%d",cnt);
// for(int i=1;i<=n;i++){
// for(int j=1;j<=n;j++){
// printf("dis[%d][%d] %lld\n",i,j,dis[i][j]);
// }
// }
}
}
鸣谢 \(lnlmz\) 小朋友提供 \(\LaTeX\) 指导与大力帮助。

浙公网安备 33010602011771号