CF1801E Gasoline prices 解题报告
当数据范围较小时,显然可以暴力合并每个点,但是当n较大时,枚举的时间复杂度会很大
但是可以发现,真正有效的合并只有n-1次,主要的时间浪费在遍历链上
所以,如何能快速找到未合并的点?
可以采用一种类似线段树的方法,利用倍增,递归求解
而合并就分成了同向和逆向,两种情况
先说同向,假设当前的两个点为u,v,它们在向上
此时,若t=0,则是这两个点未被合并,并查集合并更新l,r即可
若t>0,则递归对长度
最后,将这两段合并即可
逆向同理,但是只有一个点暴力跳父亲
代码:
#include<cstdio>
#include<algorithm>
#define ll long long
using namespace std;
const int mod=1e9+7;
int n,edgenum,head[200005],l[200005],r[200005],q;
ll ans;
ll qpow(ll x,int y)
{
ll res=1;
while(y)
{
if(y&1) res=res*x%mod;
x=x*x%mod;
y>>=1;
}
return res;
}
struct edge{
int to,nxt;
}e[400005];
void add_edge(int u,int v)
{
e[++edgenum].nxt=head[u];
e[edgenum].to=v;
head[u]=edgenum;
}
int f1[200005][23],f[200005],f2[400005][23];
int dep[200005];
void dfs(int u,int fa)
{
dep[u]=dep[fa]+1;
f[u]=u,f2[u][0]=u,f2[u+n][0]=n+u;
f1[u][0]=fa;
for(int i=1;i<=17;i++)
{
f1[u][i]=f1[f1[u][i-1]][i-1];
f2[u][i]=u,f2[u+n][i]=n+u;
}
for(int i=head[u];i;i=e[i].nxt)
{
int v=e[i].to;
if(v==fa) continue;
dfs(v,u);
}
}
int lca(int x,int y)
{
if(dep[x]<dep[y]) swap(x,y);
for(int i=17;i>=0;i--)
{
if(dep[f1[x][i]]>=dep[y])
{
x=f1[x][i];
}
if(x==y) return x;
}
for(int i=17;i>=0;i--)
{
if(f1[x][i]!=f1[y][i])
{
x=f1[x][i];
y=f1[y][i];
}
}
return f1[x][0];
}
int find(int x,int y)
{
if(f2[x][y]!=x) f2[x][y]=find(f2[x][y],y);
return f2[x][y];
}
int find1(int x)
{
if(f[x]!=x) f[x]=find1(f[x]);
return f[x];
}
void merge(int u,int v)
{
// printf("%d %d\n",u,v);
int x=find1(u),y=find1(v);
if(x==y) return;
f[x]=y;
ans=ans*qpow(max(r[x]-l[x]+1,0),mod-2)%mod;
ans=ans*qpow(max(r[y]-l[y]+1,0),mod-2)%mod;
l[y]=max(l[y],l[x]);
r[y]=min(r[y],r[x]);
ans=ans*max(0,r[y]-l[y]+1)%mod;
}
void merge1(int u,int v,int t)
{
int x=find(u,t),y=find(v,t);
if(x==y) return;
if(t==0)
{
merge(u,v);
return;
}
f2[x][t]=y;
merge1(u,v,t-1);
merge1(f1[u][t-1],f1[v][t-1],t-1);
}
int getfa(int u,int x)
{
for(int i=0;i<=17;i++)
{
if(x&(1<<i)) u=f1[u][i];
}
return u;
}
void merge2(int u,int v,int t)
{
int x=find(u,t),y=find(v+n,t);
// printf("%d %d %d %d %d\n",u,v,x,y,t);
if(x==y) return;
if(t==0)
{
merge(u,v);
return;
}
f2[x][t]=y;
f2[find(u+n,t)][t]=find(v,t);
merge2(u,f1[v][t-1],t-1);
merge2(f1[u][t-1],v,t-1);
}
int main()
{
scanf("%d",&n);
for(int i=2;i<=n;i++)
{
int x;
scanf("%d",&x);
add_edge(i,x);
add_edge(x,i);
}
dfs(1,0);
ans=1;
for(int i=1;i<=n;i++)
{
scanf("%d%d",&l[i],&r[i]);
ans=ans*(r[i]-l[i]+1)%mod;
}
// printf("%lld\n",ans);
scanf("%d",&q);
while(q--)
{
int a,b,c,d;
scanf("%d%d%d%d",&a,&b,&c,&d);
int lca1=lca(a,b),lca2=lca(c,d);
int len=min(dep[a]-dep[lca1],dep[c]-dep[lca2]);
for(int i=17;i>=0;i--)
{
if((1<<i)&len)
{
merge1(a,c,i);
a=f1[a][i],c=f1[c][i];
}
}
len=min(dep[b]-dep[lca1],dep[d]-dep[lca2]);
for(int i=17;i>=0;i--)
{
if((1<<i)&len)
{
merge1(b,d,i);
b=f1[b][i],d=f1[d][i];
}
}
if(a==lca1)
{
len=dep[b]-dep[lca1]+1;
for(int i=17;i>=0;i--)
{
if((1<<i)&len)
{
len^=(1<<i);
merge2(b,getfa(c,len),i);
b=f1[b][i];
}
}
}
else
{
len=dep[a]-dep[lca1]+1;
for(int i=17;i>=0;i--)
{
if((1<<i)&len)
{
len^=(1<<i);
merge2(a,getfa(d,len),i);
a=f1[a][i];
}
}
}
printf("%lld\n",ans);
}
return 0;
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 单元测试从入门到精通
· 上周热点回顾(3.3-3.9)
· winform 绘制太阳,地球,月球 运作规律