【树链剖分】【函数式权值分块】bzoj1146 [CTSC2008]网络管理Network

裸题,直接上。复杂度O(n*sqrt(n)*log(n))。

//Num[i]表示树中的点i在函数式权值分块中对应的点
//Map[i]表示函数式权值分块中的点i在树中对应的点
#include<cstdio>
#include<algorithm>
#include<cmath>
using namespace std;
#define N 80001
#define INF 2147483647
#define NN 87001
#define BN 296
int x,y;
int fa[N],dep[N],siz[N],son[N],Num[N],tot,top[N],n,m,Ks[N],xs[N],ys[N],w[N];
int en,first[N],next[N<<1],v[N<<1];
int en2,en3,ma[NN],a[NN];
int l[BN],sum=1,r[BN],num[N];
int r2[NN],num2[NN],sum2=1;
struct Point{int p,v;}t[NN];
bool operator < (const Point &a,const Point &b){return a.v<b.v;}
struct Val_Block
{
    int b[NN],sumv[BN];
    void insert(const int &x){++b[x]; ++sumv[num2[x]];}
    void erase(const int &x){--b[x]; --sumv[num2[x]];}
}T[285],S;
void AddEdge(const int &U,const int &V)
{
    v[++en]=V;
    next[en]=first[U];
    first[U]=en;
}
void dfs(int U,int Fa,int d)
{
    fa[U]=Fa;
    dep[U]=d;
    siz[U]=1;
    for(int i=first[U];i;i=next[i])
      if(v[i]!=fa[U])
        {
          dfs(v[i],U,d+1);
          siz[U]+=siz[v[i]];
          if(siz[v[i]]>siz[son[U]])
            son[U]=v[i];
        }
}
void dfs2(int U)
{
	if(son[U])
	  {
	  	top[son[U]]=top[U];
	  	Num[son[U]]=++tot;
	  	dfs2(son[U]);
	  }
    for(int i=first[U];i;i=next[i])
      if(v[i]!=fa[U]&&v[i]!=son[U])
        {
          top[v[i]]=v[i];
          Num[v[i]]=++tot;
          dfs2(v[i]);
        }
}
void makeblock()
{
    int sz=sqrt(n);
    if(!sz) sz=1;
    for(;sum*sz<n;++sum)
      {
        l[sum]=r[sum-1]+1;
        r[sum]=sum*sz;
        for(int i=l[sum];i<=r[sum];++i)
          num[i]=sum;
      }
    l[sum]=r[sum-1]+1;
    r[sum]=n;
    for(int i=l[sum];i<=r[sum];++i)
      num[i]=sum;
}
void val_mb()
{
    int sz=sqrt(en3);
    if(!sz) sz=1;
    for(;sum2*sz<en3;++sum2)
      {
        r2[sum2]=sum2*sz;
        for(int i=r2[sum2-1]+1;i<=r2[sum2];++i)
          num2[i]=sum2;
      }
    r2[sum2]=en3;
    for(int i=r2[sum2-1]+1;i<=r2[sum2];++i)
      num2[i]=sum2;
}
void Init_Ts()
{
    for(int i=1;i<=sum;++i)
      {
        T[i]=T[i-1];
        for(int j=l[i];j<=r[i];++j)
          T[i].insert(a[j]);
      }
}
int Query(const int &x,const int &y,const int &K)
{
//构建零散部分的权值分块
    int U=x,V=y,cnt=0,res=INF;
    int f1=top[U],f2=top[V];
    while(f1!=f2)
      {
        if(dep[f1]<dep[f2])
          {
            swap(U,V);
            swap(f1,f2);
          }
        if(num[Num[f1]]+1>=num[Num[U]])
          for(int i=Num[f1];i<=Num[U];++i)
            S.insert(a[i]);
        else
          {
            for(int i=Num[f1];i<=r[num[Num[f1]]];++i)
              S.insert(a[i]);
            for(int i=l[num[Num[U]]];i<=Num[U];++i)
              S.insert(a[i]);
          }
        U=fa[f1];
        f1=top[U];
      }
    if(dep[U]>dep[V])
      swap(U,V);
    if(num[Num[U]]+1>=num[Num[V]])
      for(int i=Num[U];i<=Num[V];++i)
        S.insert(a[i]);
    else
      {
        for(int i=Num[U];i<=r[num[Num[U]]];++i)
          S.insert(a[i]);
        for(int i=l[num[Num[V]]];i<=Num[V];++i)
          S.insert(a[i]);
      }
//计算答案
    for(int i=sum2;i>=1;--i)
      {
        int tcnt=0;
        U=x; V=y;
        f1=top[U]; f2=top[V];
        while(f1!=f2)
          {
            if(dep[f1]<dep[f2])
              {
                swap(U,V);
                swap(f1,f2);
              }
            if(num[Num[f1]]+1<num[Num[U]])
              tcnt+=T[num[Num[U]]-1].sumv[i]-T[num[Num[f1]]].sumv[i];
            U=fa[f1];
            f1=top[U];
          }
        if(dep[U]>dep[V])
          swap(U,V);
        if(num[Num[U]]+1<num[Num[V]])
          tcnt+=T[num[Num[V]]-1].sumv[i]-T[num[Num[U]]].sumv[i];
        tcnt+=S.sumv[i];
        cnt+=tcnt;
        if(cnt>=K)
          {
            cnt-=tcnt;
            for(int j=r2[i];;--j)
              {
                U=x; V=y;
                f1=top[U]; f2=top[V];
                while(f1!=f2)
                  {
                    if(dep[f1]<dep[f2])
                      {
                        swap(U,V);
                        swap(f1,f2);
                      }
                    if(num[Num[f1]]+1<num[Num[U]])
                      cnt+=T[num[Num[U]]-1].b[j]-T[num[Num[f1]]].b[j];
                    U=fa[f1];
                    f1=top[U];
                  }
                if(dep[U]>dep[V])
                  swap(U,V);
                if(num[Num[U]]+1<num[Num[V]])
                  cnt+=T[num[Num[V]]-1].b[j]-T[num[Num[U]]].b[j];
                cnt+=S.b[j];
                if(cnt>=K)
                  {
                    res=j;
                    goto OUT;
                  }
              }
          }
      }
    OUT:
//清空零散部分的权值分块
    U=x,V=y;
    f1=top[U],f2=top[V];
    while(f1!=f2)
      {
        if(dep[f1]<dep[f2])
          {
            swap(U,V);
            swap(f1,f2);
          }
        if(num[Num[f1]]+1>=num[Num[U]])
          for(int i=Num[f1];i<=Num[U];++i)
            S.erase(a[i]);
        else
          {
            for(int i=Num[f1];i<=r[num[Num[f1]]];++i)
              S.erase(a[i]);
            for(int i=l[num[Num[U]]];i<=Num[U];++i)
              S.erase(a[i]);
          }
        U=fa[f1];
        f1=top[U];
      }
    if(dep[U]>dep[V])
      swap(U,V);
    if(num[Num[U]]+1>=num[Num[V]])
      for(int i=Num[U];i<=Num[V];++i)
        S.erase(a[i]);
    else
      {
        for(int i=Num[U];i<=r[num[Num[U]]];++i)
          S.erase(a[i]);
        for(int i=l[num[Num[V]]];i<=Num[V];++i)
          S.erase(a[i]);
      }
    return res;
}
int main()
{
    scanf("%d%d",&n,&m);
    makeblock();
    for(int i=1;i<=n;++i)
      scanf("%d",&w[i]);
    for(int i=1;i<n;++i)
      {
        scanf("%d%d",&x,&y);
        AddEdge(x,y);
        AddEdge(y,x);
      }
    top[1]=1;
    Num[1]=++tot;
    dfs(1,0,1);
    dfs2(1);
    for(int i=1;i<=n;++i)
      {
        t[Num[i]].v=w[i];
        t[Num[i]].p=Num[i];
      }
    en2=n;
    for(int i=1;i<=m;++i)
      {
        scanf("%d%d%d",&Ks[i],&xs[i],&ys[i]);
        if(!Ks[i])
          {
            t[++en2].v=ys[i];
            t[en2].p=en2;
          }
      }
    sort(t+1,t+en2+1);
    ma[a[t[1].p]=++en3]=t[1].v;
    for(int i=2;i<=en2;++i)
      {
        if(t[i].v!=t[i-1].v) ++en3;
        ma[a[t[i].p]=en3]=t[i].v;
      }
    val_mb();
    Init_Ts();
    en2=n;
    for(int i=1;i<=m;++i)
      {
        if(Ks[i])
          {
            int ans=Query(xs[i],ys[i],Ks[i]);
            if(ans==INF) puts("invalid request!");
            else printf("%d\n",ma[ans]);
          }
        else
          {
            ++en2;
            for(int j=num[Num[xs[i]]];j<=sum;++j)
              {
                T[j].erase(a[Num[xs[i]]]);
                T[j].insert(a[en2]);
              }
            a[Num[xs[i]]]=a[en2];
          }
      }
    return 0;
}
时间: 2024-10-12 17:51:53

【树链剖分】【函数式权值分块】bzoj1146 [CTSC2008]网络管理Network的相关文章

hdu 5242 树链剖分找权值最大的前k条链

http://acm.hdu.edu.cn/showproblem.php?pid=5242 Problem Description It is well known that Keima Katsuragi is The Capturing God because of his exceptional skills and experience in ''capturing'' virtual girls in gal games. He is able to play k games sim

树链剖分——边上权值和

Queries On Tree Problem code: TAQTREE Tweet All submissions for this problem are available. Read problems statements in Mandarin Chinese and Russian. You are given a tree of N nodes numbered from 1 to N. The ith edge connecting node ui and vi has a w

【树链剖分】权值取反,区间最大

[题面] 有一棵n个点的树,边按照1~n-1标号,每条边拥有一个边权 现在有 m 次操作,每次操作为如下三种之一: . 1 x y:边x的权值改为y . 2 x y:将点x到点y路径上的所有边权值变成相反数 . 3 x y:查询点x到点y路径上的最大边权 第一行为两个整数n,m,表示序列长度和操作次数 接下来n-1行,每行三个数a,b,v,表示a,b之间有一条权值为v的边,按照标号1~n-1的顺序给出 接下来m行, 每行以1/2/3作为第一个数,表示操作种类.接下来两个整数,格式如题,表示一次操

BZOJ 1036 [ZJOI2008]树的统计Count (树链剖分 - 点权剖分 - 单点权修改)

题目链接:http://www.lydsy.com/JudgeOnline/problem.php?id=1036 树链剖分模版题,打的时候注意点就行.做这题的时候,真的傻了,单词拼错检查了一个多小时... 代码如下: 1 //树链剖分 点权修改 修改单节点 2 #include <iostream> 3 #include <cstring> 4 #include <algorithm> 5 #include <cstdio> 6 using namespa

HDU3966 Aragorn&#39;s Story(树链剖分 点权 模版题)

#include <iostream> #include <algorithm> #include <cstring> #include <cmath> #include <queue> #include <map> #include <set> #include <vector> #include <cstdio> using namespace std; const int N=50010; s

【函数式权值分块】【分块】bzoj3196 Tyvj 1730 二逼平衡树

#include<cstdio> #include<cmath> #include<algorithm> using namespace std; #define N 50001 #define SQRT 227 int n,m,xs[N],ys[N],ks[N],op[N],en,ma[100001],en2,a[100001]; int num[N],l[SQRT],r[SQRT],sumv[SQRT],sum=1;//分块 int num2[100001],l2[

BZOJ1146 [CTSC2008]网络管理Network 树链剖分 主席树 树状数组

欢迎访问~原文出处--博客园-zhouzhendong 去博客园看该题解 题目传送门 - BZOJ1146 题意概括 在一棵树上,每一个点一个权值. 有两种操作: 1.单点修改 2.询问两点之间的树链上的第k大值 题解 水题. 就是烦了一点. 树链剖分+带修主席树. 带修主席树: BZOJ1901 Zju2112 Dynamic Rankings 主席树 代码 #include <cstring> #include <cstdio> #include <algorithm&g

HDU 4729 An Easy Problem for Elfness(树链剖分边权+二分)

题意 链接:https://cn.vjudge.net/problem/HDU-4729 给你n个点,然你求两个点s和t之间的最大流.而且你有一定的钱k,可以进行两种操作 1.在任意连个点之间建立一个单位1的流,费用a 2.将原先的流扩大1个单位,费用b 思路 题目已经说了是一棵树,那么树上两点的最大流就是两点路径上的最小值.其实两种操作各一次对最大流的贡献是相等的.我们分类讨论: 如果a<=b,直接算第一种方案即可,直接给s.t连一条边,对答案的贡献是k/a. 如果a>b,分两种情况.如果k

bzoj1146 [CTSC2008]网络管理Network

Description M公司是一个非常庞大的跨国公司,在许多国家都设有它的下属分支机构或部门.为了让分布在世界各地的N个部门之间协同工作,公司搭建了一个连接整个公司的通信网络.该网络的结构由N个路由器和N-1条高速光缆组成.每个部门都有一个专属的路由器,部门局域网内的所有机器都联向这个路由器,然后再通过这个通信子网与其他部门进行通信联络.该网络结构保证网络中的任意两个路由器之间都存在一条直接或间接路径以进行通信. 高速光缆的数据传输速度非常快,以至于利用光缆传输的延迟时间可以忽略.但是由于路由