• 树上差分


    树上差分

    松鼠的新家

    很显然的 起点+1,终点+1,然后lca和 fa[lca] 都减 1

    最后统计答案时 因为终点被重复算了两次 ,所以要减1

    #include <iostream>
    #include <cstdio>
    #include <cstring>
    #include <algorithm>
    #include <vector>
    using namespace std;
    const int N=1000002;
    inline int read(){
    	int x=0,f=1;char ch=getchar();
    	while(!isdigit(ch)){if(ch=='-')f=-1;ch=getchar();}
    	while(isdigit(ch)){x=x*10+ch-'0';ch=getchar();}
    	return x*f;
    }
    int n,m,root,a[N];
    struct edge{
        int to,nxt;
    }e[N];
    int hd[N],tot;
    inline void add(int x,int y){
        e[++tot].to=y;e[tot].nxt=hd[x];hd[x]=tot;
    }   
    
    int son[N],top[N],dep[N],siz[N],fa[N];
    void dfs_son(int x,int f){
        siz[x]=1;fa[x]=f;dep[x]=dep[f]+1;
        for(int i=hd[x];i;i=e[i].nxt){
            int y=e[i].to;
            if(y==f) continue;
            dfs_son(y,x);
            siz[x]+=siz[y];
            if(siz[y]>siz[son[x]]) son[x]=y;
        }
    }
    
    void dfs_chain(int x,int tp){
        top[x]=tp;
        if(son[x]) dfs_chain(son[x],tp);
        for(int i=hd[x];i;i=e[i].nxt){
            int y=e[i].to;
            if(y==fa[x]||y==son[x])continue;
            dfs_chain(y,y);
        }
    }
    
    int query_lca(int x,int y){
        while(top[x]!=top[y]){
            if(dep[top[x]]>dep[top[y]]) x=fa[top[x]];
            else y=fa[top[y]];
        }
        return dep[x]<dep[y]?x:y;
    }
    int sum[N];
    void get_ans(int x){
        for(int i=hd[x];i;i=e[i].nxt){
            int y=e[i].to;
            if(y==fa[x]) continue;
            get_ans(y);
            sum[x]+=sum[y];
        }
    }
    int main(){
        n=read();
        for(int i=1;i<=n;i++) a[i]=read();
        for(int i=1,x,y;i<n;i++){
            x=read();y=read();
            add(x,y);add(y,x);
        }
        dfs_son(1,0);
        dfs_chain(1,1);
    
        for(int i=1,x,y;i<n;i++){
            int lca=query_lca(a[i],a[i+1]);
            sum[a[i]]++,sum[a[i+1]]++;
            sum[lca]--,sum[fa[lca]]--;
        }
        get_ans(1);
        for(int i=1;i<=n;i++)
            printf("%d
    ",sum[i]-(i!=a[1]));//除了起点的终点都重复计算了
        return 0;
    }
    

    当然树链剖分也可做。就是慢的要死还冗长为了复习我还是写一下吧

    #include <cstdio>
    #include <iostream>
    #include <vector>
    using namespace std;
    #define ls (p<<1)
    #define rs (p<<1|1)
    const int N=5e6;
    inline int read(){
        int x=0;char ch=getchar();
        while(!isdigit(ch)) ch=getchar();
        while(isdigit(ch)) {x=x*10+ch-'0';ch=getchar();}
        return x;
    }
    int n,val[N],ans;
    int to[N],nxt[N],hd[N],tot;
    inline void add(int x,int y){
    	to[++tot]=y;nxt[tot]=hd[x];hd[x]=tot;
    }
    
    int siz[N],dep[N],fa[N],son[N];
    void dfs_son(int x,int f){
        siz[x]=1;fa[x]=f;dep[x]=dep[f]+1;
        for(int i=hd[x];i;i=nxt[i]){
            int y=to[i];
            if(y==f) continue;
            dfs_son(y,x);
            siz[x]+=siz[y];
            if(siz[y]>siz[son[x]]) son[x]=y;
        }
    }
    int dfn[N],rev[N],top[N],dfn_cnt;
    void dfs_chain(int x,int tp){
        dfn[x]=++dfn_cnt;rev[dfn_cnt]=x;top[x]=tp;
        if(son[x]) dfs_chain(son[x],tp);
        for(int i=hd[x];i;i=nxt[i]){
            int y=to[i];
            if(dfn[y]) continue;
            dfs_chain(y,y);
        }
    }
    
    struct tree{
        int l,r;
        int tag,sum;
    }t[N];
    inline void push_up(int p){
        t[p].sum=(t[ls].sum+t[rs].sum);
    }
    void build(int l,int r,int p){
        t[p].l=l;t[p].r=r;
        if(l==r) return; 
        int mid=(l+r)>>1;
        build(l,mid,ls);
        build(mid+1,r,rs);
        push_up(p);
    }
    void push_down(int p){
        if(!t[p].tag) return;
        t[ls].tag+=t[p].tag;
        t[rs].tag+=t[p].tag;
        t[ls].sum+=(t[ls].r-t[ls].l+1)*t[p].tag;
        t[rs].sum+=(t[rs].r-t[rs].l+1)*t[p].tag;
        t[p].tag=0;
    }
    void modify(int L,int R,int v,int p){
        if(L<=t[p].l&&t[p].r<=R){
            t[p].tag+=v;
            t[p].sum+=(t[p].r-t[p].l+1)*v;
            return;
        }
        push_down(p);
        int mid=(t[p].l+t[p].r)>>1;
        if(L<=mid) modify(L,R,v,ls);
        if(R>mid) modify(L,R,v,rs);
        push_up(p);
    }
    int query(int L,int R,int p){
        if(L<=t[p].l&&t[p].r<=R) return t[p].sum;
        push_down(p);
        int mid=(t[p].l+t[p].r)>>1;
        int res=0;
        if(L<=mid) res+=query(L,R,ls);
        if(R>mid) res+=query(L,R,rs);
        return res;
    }
    void update(int x,int y,int v){
        while(top[x]!=top[y]){
            if(dep[top[x]]<dep[top[y]]) swap(x,y);
            modify(dfn[top[x]],dfn[x],v,1);
            x=fa[top[x]];
        }
        if(dfn[x]>dfn[y]) swap(x,y);
        modify(dfn[x],dfn[y],v,1);
    }
    int main(){
        n=read();
        for(int i=1;i<=n;i++) val[i]=read();
        for(int i=1,x,y;i<n;i++){
            x=read();y=read();
            add(x,y);add(y,x);
        }
        dfs_son(1,0);
        dfs_chain(1,1);
        build(1,n,1);
        for(int i=1;i<n;i++){
            int x=val[i],y=val[i+1];
            update(x,y,1);
            update(y,y,-1);
        }
        for(int i=1;i<=n;i++){
            ans=query(dfn[i],dfn[i],1);
            printf("%d
    ",ans);
        }
        return 0;
    }
    

    poj 3417

    题意:先给出一棵无根树,然后给出(m)条新边,把这(m)条边连上,然后每次你能毁掉两条边,规定一条是树边,一条是新边,问有多少种方案能使树断裂。

    附加边即非树边,主要边是树边,非树边会和树边 形成环,如果断掉路径((x,y))上的一条主要边,就需断非树边((x,y))

    我们让非树边((x,y))把树上(x)(y)的路径覆盖一次,最后只需统计每条树边被覆盖几次。

    如果被覆盖0次,那么切断它再切断任意一条附加边即可,

    如果被覆盖1次,那么断对应的条附加边即可,

    其他无论如何也切不断

    那问题转化成:给定一个无向图和生成树,求每条树边被非树边覆盖了多少次。

    树上差分

    对每条非树边((x,y))让$w[x]+1,w[y]+1,w[lca(x,y)]-2 $,然后统计以每个点为根的子树权值和 (f[x]) ,这就是(x)与其(fa)的连边被覆盖次数

    #include <cstdio>
    #include <iostream>
    using namespace std;
    inline int read(){
    	int x=0,f=1;char ch=getchar();
    	while(!isdigit(ch)){if(ch=='-')f=-1;ch=getchar();}
    	while(isdigit(ch)){x=x*10+ch-'0';ch=getchar();}
    	return f*x;
    }
    const int N=400005;
    int n,m;
    int hd[N],nxt[N],w[N],to[N],tot;
    inline void add(int x,int y,int z) {
        to[++tot]=y;w[tot]=z;nxt[tot]=hd[x];hd[x]=tot;
    }
    
    int fa[N],siz[N],son[N],dep[N];
    void dfs_son(int x,int f) {
        fa[x]=f;siz[x]=1;dep[x]=dep[f]+1;
        for(int i=hd[x];i;i=nxt[i]) {
            int y=to[i];
            if(y==f) continue;
            dfs_son(y,x);
            siz[x]+=siz[y];
            if(siz[y]>siz[son[x]]) son[x]=y;
        }
    } 
    int top[N];
    void dfs_chain(int x,int tp) {
        top[x]=tp;
        if(son[x]) dfs_chain(son[x],tp);
        for(int i=hd[x];i;i=nxt[i]) {
            int y=to[i];
            if(y==fa[x]||y==son[x]) continue;
            dfs_chain(y,y);
        }
    }
    int LCA(int x,int y) {
        while(top[x]!=top[y]) {
            if(dep[top[x]]<dep[top[y]]) swap(x,y);
            x=fa[top[x]];
        }
        return dep[x]<dep[y]?x:y;
    }
    void get_ans(int x) {
        for(int i=hd[x];i;i=nxt[i]) {
            int y=to[i];
            if(y==fa[x]) continue;
            get_ans(y);
            w[x]+=w[y];
        }
    }
    int ans;
    int main() {
        n=read();m=read();
        for(int i=1,x,y;i<n;i++) {
            x=read();y=read();
            add(x,y,0);add(y,x,0);
        }
        dfs_son(1,0);
        dfs_chain(1,1);
        for(int i=1,x,y;i<=m;i++) {
            x=read();y=read();
            w[x]++;w[y]++;w[LCA(x,y)]-=2;
        }
        get_ans(1);
        for(int i=2;i<=n;i++) {
            if(w[i]==0) ans+=m;
            if(w[i]==1) ans++;
        }
        printf("%d
    ",ans);
        return 0;
    }
    

    雨天的尾巴

    见线段树合并

  • 相关阅读:
    图的深度遍历
    判断森林中有多少棵树
    基于邻接矩阵的广度优先搜索
    第三届程序设计知识竞赛网络赛
    大数相乘
    a+b=x,ab=y
    poj3278
    不敢死队
    单链表中重复元素删除
    poj2506
  • 原文地址:https://www.cnblogs.com/ke-xin/p/13544559.html
Copyright © 2020-2023  润新知