树上差分

T1 Max Flow P

树上差分学习,为了做大根堆刷了这么一道树上差分板子题

//Max Flow P
//用来练练树上差分
//最后答案只能用遍历
//lca的话用tarjan吧
//算了,tarjan必须离线,用倍增吧
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#include <cmath>
using namespace std;
const int mx=500000+100;
bool vis[mx];
int len,n,k,head[mx];
int du[mx],kk,tim;
int f[mx][20];
int dif[mx];
int ans;
int an[mx];
struct Node{
    int from;
    int to;
    int next;
}e[mx*3];
inline int read(){
    int w=1;
    int x=0;
    char ch=getchar();
    while(isdigit(ch)==false){
        if(ch=='-')w=-1;
        ch=getchar();
    }
    while(isdigit(ch)==true){
        x=(x<<3)+(x<<1)+(ch^48);
        ch=getchar();
    }
    return x*w;
}
void Insert(int u,int v){
    e[++len].from=u;
    e[len].to=v;
    e[len].next=head[u];
    head[u]=len;
}
void dfs(int u){
    vis[u]=1;
    for(int i=head[u];i;i=e[i].next){
        int v=e[i].to;
        if(vis[v]==0){
            vis[v]=1;
            du[v]=du[u]+1;
         //   printf("du[1]=%d\n",du[1]);
            f[v][0]=u;
            for(int j=1;j<=kk;++j){
                f[v][j]=f[f[v][j-1]][j-1];
            }//dp要从祖先那里拿值,所以先dp再dfs
            dfs(v);
        }
    }
}
void dfss(int u){
    an[u]+=dif[u];
    vis[u]=1;
 //   printf("an[%d]=%d\n",u,an[u]);
    for(int i=head[u];i;i=e[i].next){
        int v=e[i].to;
    //    printf("vis[%d]=%d\n",v,vis[v]);
        if(vis[v]==0){
            vis[v]=1;
            dfss(v);
            an[u]+=an[v];
        }
    }
}
int LCA(int x,int y){
  //  printf("du[x]=%d %d\n",du[x],du[y]);
    if(du[x]>du[y])swap(x,y);
    for(int j=kk;j>=0;--j){
    //    printf("%d %d %d y=%d j=%d\n",f[y][j],du[f[y][j]],du[x],y,j);
        if(du[f[y][j]]>=du[x])y=f[y][j];//等于号
    }//把y的深度拉到与x同一水平,此刻再往上走一个就是x,y的lca
    //每次是log(n)级别的查询,对了,log默认是以为底,所以要换底
 //   printf("y=%d\n",y);
    if(x==y)return x;
    else {
        for(int j=kk;j>=0;--j){
            if(f[x][j]!=f[y][j]){
                x=f[x][j];
                y=f[y][j];
                //这里再附上那个很经典的话:为什么不能当f[x][k]==f[y][k]直接输出答案,因为我们求的是最近公共祖先 
                //注意再次体会这里,此刻x,和y的深度一样,大的不相等小的肯定不相等啊,如果正序,只要这次相等了后面肯定都相等
                //所以正序倒序无所谓啊,我们是不等于才转移
            }
        }
    //    printf("x=%d\n",x);
        return f[x][0];
    }
}
void Solve(){
    n=read();k=read();
    kk=log(n)/log(2)+1;
    for(int i=1;i<=n-1;++i){
        int x=read(),y=read();
        Insert(x,y);//边建立单向的应该就够了
        //是要建立双向的,只是你以为以1为根,它给的数据可不一样
        //所以才用vis
        Insert(y,x);
    }
    du[1]=1;
    f[1][0]=1;
    //我觉得就是得特判1,不是
    dfs(1);
    memset(vis,0,sizeof(vis));
    for(int i=1;i<=k;++i){
        int x=read();
        int y=read();
        int lc=LCA(x,y);
   //     printf("lc=%d\n",lc);
        //差分
        
        dif[x]++;dif[y]++;dif[lc]--;dif[f[lc][0]]-- ;//树上差分
        if(lc==1)dif[1]++;
        //1这里确实应该这么写一下
        //再次回顾定义:一个点的真实权值是一个点子树内所有差分后的权值之和
        //它这个定义不太好,我换个说法:一个点的权值是它的原本权值加上它的子树的每个点的差分数组的值的和(包括它本身)
        //这个是按点差分
        //如果按边差分:
        //每个节点最多只有一个父节点,也就是向上连的边只有一条,所以我们就把一条边的边权转化成子节点的点权(根节点没有点权)
        //这里注意,每个点所代表的点权是它向上连的那一条边,所以这里lca-=2,lc的父亲不用更改
        //这个其实一手摸就理解了
    }
    dfss(1);
    for(int i=1;i<=n;++i){//它有n-1个边,n个点啊啊啊啊啊啊,你个智障
    //    printf("an[%d]=%d\n",i,an[i]);
        ans=max(ans,an[i]);
    }
    printf("%d\n",ans);
}
int main(){
    Solve();
    return 0;
}
View Code

 

posted @ 2022-04-15 11:43  SMTwy  阅读(29)  评论(0编辑  收藏  举报