The 2024 ICPC Asia EC Regionals Online Contest (II) - Problem B. Mountain Booking

Claris發表於2024-09-21

從 $1$ 到 $m$ 依次考慮每個日期。假設當前正在考慮第 $i$ 天,那麼只有第 $i$ 天來訪的遊客以及指定第 $i$ 天的查詢是有用的。將這些遊客和查詢都提取出來,透過 Kruskal 重構樹可以很方便地在 $O(n\log n)$ 的時間內計算出這些查詢的答案。

不幸的是,本題還有加邊刪邊操作,無法輕易地動態維護 Kruskal 重構樹。解決問題的關鍵是注意到假設第 $i$ 天有 $t_i$ 個遊客、$q_i$ 個詢問,那麼可以支付 $O((t_i+q_i)\log n)$ 的代價來獲取它們對應的節點形成的大小為 $O(t_i+q_i)$ 的虛樹,然後在虛樹上暴力構建 Kruskal 重構樹計算每個詢問的答案。

求虛樹的方法很多,比如 LCT 或者離線分治。假設 $n,m,p,q$ 同階,總時間複雜度為 $O(n\log n)$。

#include<iostream>
#include<vector>
#include<algorithm>
using namespace std;
typedef long long ll;
const int N=100005,M=200005,Q=200005,K=19;
vector<int>gt[M],gq[M];
struct Qry{int x;ll ans;}qry[Q];
struct E{int x,y,w;}e[K][N],edge[N];
namespace Inner{
int f[N<<1],tour[N<<1];ll sum[N<<1];
inline bool cmp(const E&a,const E&b){return a.w<b.w;}
int F(int x){
  if(f[x]==x)return x;
  int y=f[x];
  f[x]=F(f[x]);
  sum[x]+=sum[y];
  return f[x];
}
inline void solve(int n,int m,int d){
  int i;
  for(i=1;i<=n;i++){
    f[i]=i;
    tour[i]=sum[i]=0;
  }
  for(const auto&o:gt[d])tour[o]++;
  sort(edge+1,edge+m+1,cmp);
  for(i=1;i<=m;i++){
    int x=edge[i].x,y=edge[i].y,w=edge[i].w;
    x=F(x),y=F(y);
    int z=n+i;
    sum[x]=1LL*w*tour[y];
    sum[y]=1LL*w*tour[x];
    sum[z]=0;
    f[x]=f[y]=f[z]=z;
    tour[z]=tour[x]+tour[y];
  }
  for(const auto&o:gq[d]){
    int x=qry[o].x;
    F(x);
    qry[o].ans=sum[x];
  }
}
}
struct Seg{int x,y,w,l,r;}seg[K][N+M];
int n,m,ct,cq,ce,i,x,y,z,o;
int g[N],v[N<<1],w[N<<1],nxt[N<<1],ed,fa[N],wei[N],id[N],vip[N];bool vis[N];
inline void add(int x,int y,int z){
  v[++ed]=y;
  w[ed]=z;
  nxt[ed]=g[x];
  g[x]=ed;
}
inline void addedge(int x,int y,int z){
  add(x,y,z);
  add(y,x,z);
}
inline void newedge(int x,int y,int z){
  edge[++ed].x=x;
  edge[ed].y=y;
  edge[ed].w=z;
}
void compress(int x,int y){
  int d=0;
  id[x]=0;
  vis[x]=1;
  for(int i=g[x];i;i=nxt[i]){
    int u=v[i];
    if(u==y)continue;
    fa[u]=x;
    wei[u]=w[i];
    compress(u,x);
    if(!id[u])continue;
    d++;
    id[x]^=id[u];
  }
  if(d>1)vip[x]=1;
  if(vip[x]){
    for(int i=g[x];i;i=nxt[i]){
      int u=v[i];
      if(u==y)continue;
      int t=id[u];
      if(!t)continue;
      int mx=0;
      for(int j=t;j!=x;j=fa[j])if(mx<wei[j])mx=wei[j];
      newedge(x,t,mx);
    }
    id[x]=x;
  }
}
void solve(int d,int l,int r,int ce,int n,int m){
  int i;
  ed=0;
  for(i=1;i<=n;i++)vip[i]=vis[i]=g[i]=0;
  for(i=1;i<=m;i++)addedge(e[d][i].x,e[d][i].y,e[d][i].w);
  for(i=1;i<=ce;i++){
    if(seg[d][i].r<l||seg[d][i].l>r)continue;
    if(seg[d][i].l<=l&&seg[d][i].r>=r){
      addedge(seg[d][i].x,seg[d][i].y,seg[d][i].w);
      continue;
    }
    vip[seg[d][i].x]=vip[seg[d][i].y]=1;
  }
  for(i=l;i<=r;i++){
    for(const auto&o:gt[i])vip[o]=1;
    for(const auto&o:gq[i])vip[qry[o].x]=1;
  }
  ed=0;
  for(i=1;i<=n;i++)if(vip[i]&&!vis[i])compress(i,0);
  int _n=0;
  for(i=1;i<=n;i++)if(vip[i])vip[i]=++_n;
  if(_n<=1)return;
  n=_n;
  m=ed;
  for(i=1;i<=m;i++){
    edge[i].x=vip[edge[i].x];
    edge[i].y=vip[edge[i].y];
  }
  for(i=l;i<=r;i++){
    for(auto&o:gt[i])o=vip[o];
    for(const auto&o:gq[i])qry[o].x=vip[qry[o].x];
  }
  if(l==r){
    Inner::solve(n,m,l);
    return;
  }
  int _ce=0;
  for(i=1;i<=m;i++)e[d+1][i]=edge[i];
  for(i=1;i<=ce;i++){
    if(seg[d][i].r<l||seg[d][i].l>r)continue;
    if(seg[d][i].l<=l&&seg[d][i].r>=r)continue;
    seg[d+1][++_ce].x=vip[seg[d][i].x];
    seg[d+1][_ce].y=vip[seg[d][i].y];
    seg[d+1][_ce].w=seg[d][i].w;
    seg[d+1][_ce].l=seg[d][i].l;
    seg[d+1][_ce].r=seg[d][i].r;
  }
  int mid=(l+r)>>1;
  solve(d+1,l,mid,_ce,n,m);
  solve(d+1,mid+1,r,_ce,n,m);
}
int main(){
  ios_base::sync_with_stdio(0);cin.tie(0);
  cin>>n>>m>>ct>>cq;
  for(i=1;i<n;i++){
    cin>>x>>y>>z;
    seg[0][i].x=x;
    seg[0][i].y=y;
    seg[0][i].w=z;
    seg[0][i].l=1;
    seg[0][i].r=m;
  }
  ce=n-1;
  for(i=1;i<=m;i++){
    cin>>o>>x>>y>>z;
    seg[0][o].r=i-1;
    seg[0][++ce].x=x;
    seg[0][ce].y=y;
    seg[0][ce].w=z;
    seg[0][ce].l=i;
    seg[0][ce].r=m;
  }
  for(i=1;i<=ct;i++){
    cin>>o>>x;
    gt[o].emplace_back(x);
  }
  for(i=1;i<=cq;i++){
    cin>>o>>x;
    qry[i].x=x;
    gq[o].emplace_back(i);
  }
  solve(0,1,m,ce,n,0);
  for(i=1;i<=cq;i++)cout<<qry[i].ans<<"\n";
}

  

相關文章