算法学习:树上差分

初步学习了树上差分,这里主要是些例题。

\(Part1\). 树上点的差分:

题目链接:P3128 [USACO15DEC]最大流Max Flow
还以为是网络流
点的差分很简单,就是树剖不配线段树了,一个差分数组就够了:
复杂度\(O(nlogn+k+n)\)(大概是),可以通过本题。

\(Code\):

#include<iostream>
#include<cstdio>
#include<cmath>
using namespace std;
const int MAXN=500005;
int f[MAXN],top[MAXN],tot[MAXN],son[MAXN],deep[MAXN];
int rt[MAXN];
int id[MAXN],c=0;
int n,m,l,r;
struct node
{
    int to,nxt;
}e[MAXN<<1];
int head[MAXN],cnt=0;
void add(int u,int v)
{
    e[++cnt].to=v;
    e[cnt].nxt=head[u];
    head[u]=cnt;
}
int dfs1(int cur,int fa,int step)
{
    deep[cur]=step;
    f[cur]=fa;
    tot[cur]=1;
    int maxn=-1;
    for(int i=head[cur];i;i=e[i].nxt)
    {
        int j=e[i].to;
        if(j!=fa)
        {
            tot[cur]+=dfs1(j,cur,step+1);
            if(tot[j]>maxn) maxn=tot[j],son[cur]=j;
        }
    }
    return tot[cur];
}
void dfs2(int cur,int topf)
{
    top[cur]=topf;
    id[cur]=++c;
    if(!son[cur]) return;
    dfs2(son[cur],topf);
    for(int i=head[cur];i;i=e[i].nxt)
    {
        int j=e[i].to;
        if(!id[j]) dfs2(j,j);
    }
}
void D_in_tree(int l,int r)
{
    while(top[l]!=top[r])
    {
        if(deep[top[l]]<deep[top[r]]) swap(l,r);
        rt[id[l]+1]--;
        rt[id[top[l]]]++;
        l=f[top[l]];
    }
    if(deep[l]<deep[r]) swap(l,r);
    rt[id[r]]++;
    rt[id[l]+1]--;
    return;
}
int main()
{
    scanf("%d%d",&n,&m);
    for(int i=1;i<n;i++)
    {
        scanf("%d%d",&l,&r);
        add(l,r);
        add(r,l);
    }
    dfs1(1,1,1);
    dfs2(1,1);
    for(int i=1;i<=n;i++) rt[i]=0;
    for(int i=1;i<=m;i++)
    {
        scanf("%d%d",&l,&r);
        D_in_tree(l,r);
        //for(int i=1;i<=n;i++) cout<<rt[i]<<" ";
        //cout<<endl;
    }
    int last=0,ans=-1;
    /*for(int i=1;i<=n;i++) cout<<id[i]<<" ";
    cout<<endl;
    for(int i=1;i<=n;i++) cout<<top[i]<<" ";
    cout<<endl;
    for(int i=1;i<=n;i++) cout<<deep[i]<<" ";
    cout<<endl;
    for(int i=1;i<=n;i++) cout<<rt[i]<<" ";
    cout<<endl;*/
    for(int i=1;i<=n;i++)
    {
        rt[i]=last+rt[i];
        last=rt[i];
        ans=max(ans,last);
    }
    printf("%d\n",ans);
    return 0;
}
/*
6 6
1 2
3 1
1 4
3 5
6 3
2 4
2 5
5 6
1 3
5 4
6 3
*/

当然有很多的调试语句,突出惨烈性,又附了组数据。

\(Part2\).树上边的差分

题目链接:CF191C Fools and Roads
大家都用的\(lca\),而我有一个好法子
这棵树是无根树,但并不影响答案,所以我们认为她是有根树(以1为根),这样我们发现可对边重新编号:
即边的序号为连接深度较大的节点编号,显然为\(2,3......n\).
于是差分即可:

\(Code\):

#include<iostream>
#include<cstdio>
#include<cmath>
using namespace std;
const int MAXN=1000005;
int f[MAXN],top[MAXN],tot[MAXN],son[MAXN],deep[MAXN];
int rt[MAXN];
int id[MAXN],c=0;
int n,m,l,r;
struct node
{
    int to,nxt,ans;
    node()
    {
        ans=-1;
    }
}e[MAXN<<1];
int head[MAXN],cnt=0;
void add(int u,int v)
{
    e[++cnt].to=v;
    e[cnt].nxt=head[u];
    head[u]=cnt;
}
int dfs1(int cur,int fa,int step)
{
    deep[cur]=step;
    f[cur]=fa;
    tot[cur]=1;
    int maxn=-1;
    for(int i=head[cur];i;i=e[i].nxt)
    {
        int j=e[i].to;
        if(j!=fa)
        {
            tot[cur]+=dfs1(j,cur,step+1);
            if(tot[j]>maxn) maxn=tot[j],son[cur]=j;
        }
    }
    return tot[cur];
}
void dfs2(int cur,int topf)
{
    top[cur]=topf;
    id[cur]=++c;
    if(!son[cur]) return;
    dfs2(son[cur],topf);
    for(int i=head[cur];i;i=e[i].nxt)
    {
        int j=e[i].to;
        if(!id[j]) dfs2(j,j);
    }
}
void D_in_tree(int l,int r)
{
    while(top[l]!=top[r])
    {
        if(deep[top[l]]<deep[top[r]]) swap(l,r);
        rt[id[l]+1]--;
        rt[id[top[l]]]++;
        l=f[top[l]];
    }
    if(deep[l]<deep[r]) swap(l,r);
    rt[id[son[r]]]++;
    rt[id[l]+1]--;
    return;
}
void work(int cur,int fa)
{
    for(int i=head[cur];i;i=e[i].nxt)
    {
        int j=e[i].to;
        if(j==fa) continue;
        e[i].ans=rt[id[j]];
        work(j,cur);
    }
}
int main()
{
    //freopen("data.in","r",stdin);
    //freopen("baoli.out","w",stdout);
    scanf("%d",&n);
    if(n==0)
    {
        cout<<0;
        return 0;
    }
    for(int i=1;i<n;i++)
    {
        scanf("%d%d",&l,&r);
        add(l,r);
        add(r,l);
    }
    dfs1(1,1,1);
    dfs2(1,1);
    for(int i=1;i<=n;i++) if(!son[i]) son[i]=i;
    scanf("%d",&m);
    for(int i=1;i<=n;i++) rt[i]=0;
    for(int i=1;i<=m;i++)
    {
        scanf("%d%d",&l,&r);
        if(l!=r) D_in_tree(l,r);
    }
    for(int i=1;i<=cnt;i++) e[i].ans=-1;
    work(1,1);
    for(int i=1;i<=cnt;i++) if(e[i].ans>=0) printf("%d ",e[i].ans);
    printf("\n");
    return 0;
}

似乎和\(lys\)大佬数据对拍有锅,但我也不太确定是不是对拍器炸了(但愿是)。
反正难写就是了,
再不会\(Crayon\)的前提下,\(K...\)算法随机生成出了锅,鸣谢一位大佬的程序,附上:

\(Code\):

#include<bits/stdc++.h>
using namespace std;
int n,m,cnt,fa[100015];
int find(int x){
    return x==fa[x]?x:fa[x]=find(fa[x]);
}
int main()
{
    freopen("data.in","w",stdout);
    srand((unsigned)time(NULL));
    //srand(time(0));
    //int n=rand()%6+1;
    int n=rand()%30+2;
    cout<<n<<endl;
    for(int i=1;i<=n;i++)fa[i]=i;
    while(cnt<n-1){
        int x=rand()*rand(),y=rand()*rand();
        x=x%n+1;
        y=y%n+1;
        int x1=find(x),y1=find(y);
        if(x1!=y1) fa[x1]=y1,cnt++,cout<<x<<" "<<y<<endl;
    }
    int q=rand()%75+1;
    cout<<q<<"\n";
    for(int i=1;i<=q;i++)
    {
        int a=rand()%n+1,b=rand()%n+1;
        cout<<a<<" "<<b<<"\n";
    }
    return 0;
}

这样并查集就实现了随机生成树

猜你喜欢

转载自www.cnblogs.com/tlx-blog/p/12325721.html
今日推荐