Codeforces 526G

100 篇文章 0 订阅
27 篇文章 1 订阅

这题之前被搬过。
考虑以 x x x为根,那么我们显然会选 k k k个叶子( k k k的值取决于 x x x是否是叶子),使得它们到 x x x的路径的并长度最大。容易证明一个正确的贪心是以 x x x为根做长链剖分后取前 k k k大的链。
但是这样复杂度太高了,注意到我们一定会选直径的一端。那么我们分别以直径两端建树,取答案的 max ⁡ \max max,求出依次选择的方案,如果前 k k k大的链覆盖了 x x x,那么答案显然一致,否则我们会在 x x x所在的子树中取一个叶子对应的链,删去另一个已选的叶子对应的链,可以讨论一下用线段树维护。
时间复杂度 O ( ( n + q ) log ⁡ n ) \mathcal O((n+q)\log n) O((n+q)logn)

#include <bits/stdc++.h>
#define inf 0x3f3f3f3f
#define FR first
#define SE second
#define end end2
 
using namespace std;
 
typedef pair<int,int> pr;
 
struct Edge {
  int t,v,next;
  Edge() {}
  Edge(int a,int b,int c):t(a),v(b),next(c) {}
};
 
Edge e[200005];
int head[100005];
 
namespace Tree {
 
int dis[100005];
 
int dfs(int x,int fa) {
  int ans=x;
  for(int i=head[x];i;i=e[i].next)
    if (e[i].t!=fa) {
        int u=e[i].t;
        dis[u]=dis[x]+e[i].v;
        int t=dfs(u,x);
        if (dis[t]>=dis[ans]) ans=t;
    }
  return ans;
}
 
}
 
struct SGT {
 
pr maxn[400000];
int addv[400000];
 
inline void pushdown(int o) {
  if (addv[o]) {
    addv[o*2]+=addv[o];maxn[o*2].FR+=addv[o];
    addv[o*2+1]+=addv[o];maxn[o*2+1].FR+=addv[o];
    addv[o]=0;
  }
}
 
inline void pushup(int o) {
  maxn[o]=max(maxn[o*2],maxn[o*2+1]); 
}
 
void build(int l,int r,int o,int *fir) {
  addv[o]=0;
  if (l==r) maxn[o]=pr(fir[l],l);
  else {
    int m=((l+r)>>1);
    build(l,m,o*2,fir);
    build(m+1,r,o*2+1,fir);
    pushup(o);
  }
}
 
void update(int l,int r,int o,int lx,int rx,int p) {
  if (l>=lx&&r<=rx) {
    addv[o]+=p;
    maxn[o].FR+=p;
  }
  else {
    pushdown(o);
    int m=((l+r)>>1);
    if (m>=lx) update(l,m,o*2,lx,rx,p);
    if (m<rx) update(m+1,r,o*2+1,lx,rx,p);
    pushup(o);
  }
}
 
pr query() {
  return maxn[1];
}
 
};
 
struct Solve {
 
int rt,col[100005],col2[100005],fa[100005][20];
int maxd[100005],dis[100005],pv[100005];
bool leaf[100005];
 
int dfn[100005],end[100005],dfs_cnt;
int num[100005],fir[100005];
 
void dfs(int x) {
  dfn[x]=dfs_cnt+1;
  leaf[x]=1;
  for(int i=head[x];i;i=e[i].next)
    if (e[i].t!=fa[x][0]) {
        leaf[x]=0;
        int u=e[i].t;
        fa[u][0]=x;
        for(int j=1;j<20;j++) fa[u][j]=fa[fa[u][j-1]][j-1];
        dis[u]=dis[x]+e[i].v;pv[u]=e[i].v;
        dfs(u);
        maxd[x]=max(maxd[x],maxd[u]+e[i].v);
    }
  if (leaf[x]) {
    num[++dfs_cnt]=x;
    fir[dfs_cnt]=dis[x];
  }
  end[x]=dfs_cnt;
}
 
SGT tr;
int val[100005],sum[100005],cur[100005];
 
void build(int t) {
  rt=t;
  dfs(rt);
  tr.build(1,dfs_cnt,1,fir);
  for(int i=1;i<=dfs_cnt;i++) {
    pr t=tr.query();
    val[i]=t.FR;sum[i]=sum[i-1]+val[i];
    int x=num[t.SE];
    cur[i]=x;
    tr.update(1,dfs_cnt,1,dfn[x],end[x],-inf);
    while (x!=rt&&!col[x]) {
        tr.update(1,dfs_cnt,1,dfn[x],end[x],-pv[x]);
        col[x]=i;
        x=fa[x][0];
      }
    while (x!=rt&&!col2[x]) {
        col2[x]=i;
        x=fa[x][0];
    }
  }
}
 
int jump(int x,int d) {
  for(int i=19;i>=0;i--)
    if (col[fa[x][i]]>=d) x=fa[x][i];
  return x;
}
 
int query(int x,int y) {
  y=min(2*y-1,dfs_cnt);
  int ans=sum[y];
  if (col[x]>y) {
    int u=fa[jump(x,y)][0];
    ans+=maxd[x]+dis[x]-dis[u];
    int s=val[y];
    if (u!=rt&&(!col2[u]||col2[u]>y)) {
        int v=cur[col[u]];
        s=min(s,dis[v]-dis[u]);
      }
    ans-=s;
  }
  return ans;
}
 
} a,b;
 
int main() {
  int n,m;
  scanf("%d%d",&n,&m);
  for(int i=1;i<n;i++) {
    int x,y,z;
    scanf("%d%d%d",&x,&y,&z);
    e[2*i-1]=Edge(y,z,head[x]);
    head[x]=2*i-1;
    e[2*i]=Edge(x,z,head[y]);
    head[y]=2*i;
  }
  int rt1=Tree::dfs(1,0);
  Tree::dis[rt1]=0;
  int rt2=Tree::dfs(rt1,0);
  a.build(rt1);
  b.build(rt2);
  int lastans=0;
  for(int i=1;i<=m;i++) {
    int x,y;
    scanf("%d%d",&x,&y);
        x=(x+lastans-1)%n+1;y=(y+lastans-1)%n+1;
    if (n==1) {
        puts("0");
        continue;
      }
    lastans=max(a.query(x,y),b.query(x,y));
    printf("%d\n",lastans);
  }
  return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值