树上差分

树上差分

松鼠的新家

很显然的 起点+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\n",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\n",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\n",ans);
    return 0;
}

雨天的尾巴

见线段树合并

posted @ 2020-08-22 10:13  ke_xin  阅读(41)  评论(0编辑  收藏  举报