2025省选模拟5 T1
看到大家使用优美的记搜方法过掉 T1,我来介绍一种较为独特的方法。
众所周知,我们只需要处理 \(2m\) 个接口(以下称为关键点)。我们将每个关键点拆分成 \(O(n)\) 条信息,分别记录这个点在每个阶段所在的树以及从上个状态到下一阶段是从前半子树(标号不增加的子树)还是后半子树合并来的。例如:我们先把 \(T_0\) 的 \(0\) 号节点连到自己形成 \(T_1\),经过一系列操作后将 \(T_1\) 的 \(1\) 号节点和 \(T_a\) 的某个节点连接形成 \(T_b\),最终 \(T_1\) 的 \(1\) 号节点在 \(T_b\) 上成为了关键点,则我们记录的各个状态所在的树的信息为 \(0 \rightarrow 1 \rightarrow b\),分别是作为后半子树和前半子树合并的。这里有个反常的细节:对于两个不同关键点,如果他们可以到达同一状态,这个状态应当记录多次而非一次。这是一个显然的性质,因为我们把不同关键点的完整前驱状态序列独立地存了起来,但这是我们未来操作正确性的重要条件。
考虑 \(T_a\) 和 \(T_b\) 合并为 \(T\):答案等于 \(T_a\) 和 \(T_b\) 的答案加上两颗树以其对应的接口(即关键点)为根所有点的深度之和与对面点的个数的乘积(分解 \((a+c)+(a+d)+(b+c)+(b+d)=2(a+b)+2(c+d)\),其中 \(a,b\) 为 \(T_a\) 中的,\(c,d\) 反之),再将上两颗树的大小乘积(新形成的路径条数)乘上新边权值。某个点的深度之和需要加上另一棵树接口处的深度之和加上另一棵树节点个数乘上这个关键点与另一棵树接口间的距离。综上所述,我们需要维护每棵树的答案,大小,每个关键点为根的深度之和与两个关键点间距离。前两者很容易维护。先来考虑点间距离。对于每个关键点,维护指针表示这个点的当前状态。每次找到下一状态是 \(T\) 且不是接口的节点,分别加入前半棵树或后半棵树的集合。每次取出两个集合中的点更新另一集合中的每一个点(可以不用处理除了另一棵树内下一状态是 \(T\) 的点以外的点,因为这样的点未来一定与前者无关,即使他是错的也不影响答案。)。更新方式为用两点加上到本树的接口距离再加上新边权值。接下来更新子树内深度和的过程就很显然。为什么要排掉接口?因为这两个接口未来不会再用,对答案没有影响,算上可能会有意想不到的错误(比如合并同一颗树,可能导致同一接口有一个值为新加边的到自己的距离,使得后面距离多加)。
这个解法无需使用递归、哈希表,时间 \(O(n^3)\),空间\(O(n^2)\),虽说码量稍微大些。
Code
#include<bits/stdc++.h>
using namespace std;
const long long p=1000000007;
struct node{
bool opt[800];
int tr[800],tot,cur;
}dot[800];
struct stct{
int sttr,edtr,st,ed;
long long stpt,edpt,val;
}ask[800];
int n,cnt;
long long size[512],sdep[1024],dist[800][800],szst[512],ans[512];
inline void gen(int x,long long x1,int y){
dot[++cnt].tr[0]=y;
for(long long tx=x,tx1=x1;tx;){
dot[cnt].tr[++dot[cnt].tot]=tx;
if(tx1>=szst[tx]) tx1-=szst[tx],tx=ask[tx].edtr;
else tx=ask[tx].sttr,dot[cnt].opt[dot[cnt].tot]=1;
}
++dot[cnt].tot;
int ulim=dot[cnt].tot>>1;
for(int i=0;i<=ulim;++i){
swap(dot[cnt].tr[i],dot[cnt].tr[dot[cnt].tot-i]);
swap(dot[cnt].opt[i],dot[cnt].opt[dot[cnt].tot-i]);
}
}
vector<int> v1,v2;
int main(){
freopen("loquat.in","r",stdin);
freopen("loquat.out","w",stdout);
scanf("%d",&n);
size[0]=1;
for(int i=1;i<=n;++i){
scanf("%d%d%lld%lld%lld",&ask[i].sttr,&ask[i].edtr,&ask[i].stpt,&ask[i].edpt,&ask[i].val);
szst[i]=size[ask[i].sttr];
size[i]=szst[i]+size[ask[i].edtr];
gen(ask[i].sttr,ask[i].stpt,i);
gen(ask[i].edtr,ask[i].edpt,i);
}
memset(size,0,sizeof(size));
size[0]=1;
for(int i=1,x=1,y=2;i<=n;++i,x+=2,y+=2){
int tx=ask[i].sttr,ty=ask[i].edtr;
ans[i]=((((sdep[x]*size[ty]%p+sdep[y]*size[tx]%p)%p+ans[tx])%p+ans[ty])%p+size[tx]*size[ty]%p*ask[i].val%p)%p;
printf("%lld\n",ans[i]);
size[i]=(size[tx]+size[ty])%p;
for(int j=1;j<=cnt;++j){
if(j==x||j==y) continue;
if(dot[j].tr[dot[j].cur+1]==i){
if(dot[j].opt[dot[j].cur+1]) v1.emplace_back(j);
else v2.emplace_back(j);
}
}
for(auto j:v1){
dist[j][y]=dist[j][x]+ask[i].val;
if(dist[j][y]>=p) dist[j][y]-=p;
dist[y][j]=dist[j][y];
for(auto k:v2){
dist[j][k]=dist[j][x]+dist[k][y];
if(dist[j][k]>=p) dist[j][k]-=p;
dist[j][k]+=ask[i].val;
if(dist[j][k]>=p) dist[j][k]-=p;
dist[k][j]=dist[j][k];
}
}
for(auto j:v2){
dist[j][x]=dist[j][y]+ask[i].val;
if(dist[j][x]>=p) dist[j][x]-=p;
dist[x][j]=dist[j][x];
for(auto k:v1){
dist[j][k]=dist[j][y]+dist[k][x];
if(dist[j][k]>=p) dist[j][k]-=p;
dist[j][k]+=ask[i].val;
if(dist[j][k]>=p) dist[j][k]-=p;
dist[k][j]=dist[j][k];
}
}
for(auto j:v1){
sdep[j]=((sdep[j]+dist[j][y]*size[ty]%p)%p+sdep[y])%p;
++dot[j].cur;
}
for(auto j:v2){
sdep[j]=((sdep[j]+dist[j][x]*size[tx]%p)%p+sdep[x])%p;
++dot[j].cur;
}
v1.clear();
v2.clear();
}
return 0;
}

浙公网安备 33010602011771号