zoukankan      html  css  js  c++  java
  • D6差分及树上差分

    原谅我这篇博客拖了很久才写;

    来到学校就和白痴一样缺了一世纪的课 上课特别懵;还有开学考枯了;

    差分有列的差分,对于一段区间【l,r】进行修改,显然如果我们对于他的差分数组的l和r+1进行修改就可以了;

    Xni=1a[i]

    =(c[1]) + (c[1] + c[2]) + · · · + (c[1] + c[2] + · · · + c[n])

    = n × c[1] + (n − 1) × c[2] + · · · + c[n]= n × (c[1] + c[2] + · · · + c[n])− (0 × c[1] + 1 × c[2] + · · · + (n − 1) × c[n])

    所以,我们维护一个数组c2[i] = (i − 1) × c[i]在将区间[l,r] 的数全部+v 则还需同时将c2[l] + v × (i − 1), c2[r + 1] + (−v) × r 。

    树上差分:分为点的差分和边的差分;

    点的差分:

    在一棵n个结点的树中,形容从si走到到ti的要求,求这条路径上的点被经过的次数。

    显然,我们需要找到他们的LCA(中转点)。

    我们需要让cnt[s] + +,让cnt[t] + +,而让他们的cnt[lca] − −,cnt[faher[lca]] − −;

    最终统计即可;

    边的差分不太一样;cnt[s] + +, cnt[t] + +, cnt[LCA]− =2;仔细画图理解一下;

    第一题:BZOJ 4390

    树上差分的模板题:

    #include<bits/stdc++.h>
    using namespace std;
    const int maxn=5e4+10;
    template<typename T>inline void read(T &x)
    {
        x=0;
        T f=1,ch=getchar();
        while (!isdigit(ch)) ch=getchar();
        if (ch=='-') f=-1, ch=getchar();
        while (isdigit(ch)) x=(x<<1)+(x<<3)+(ch^48), ch=getchar();
        x*=f;
    }
    int ver[maxn<<1],Next[maxn<<1],head[maxn],len;
    inline void add(int x,int y)
    {
        ver[++len]=y,Next[len]=head[x],head[x]=len;
    }
    int fa[maxn],d[maxn],f[maxn][21];
    inline void dfs1(int x,int father,int deep)
    {
        fa[x]=father,d[x]=deep;
        for (int i=1;i<16;++i)
            f[x][i]=f[f[x][i-1]][i-1];
        for (int i=head[x];i;i=Next[i])
        {
            int y=ver[i];
            if (y==father) continue;
            f[y][0]=x;
            dfs1(y,x,deep+1);
        }
    }
    inline int lca(int x,int y)
    {
        if (d[x]>d[y]) swap(x,y);
        for (int i=15;i>=0;--i)
            if (d[f[y][i]]>=d[x])
                y=f[y][i];
        if (x==y) return x;
        for (int i=15;i>=0;--i)
            if (f[x][i]!=f[y][i])
                x=f[x][i],y=f[y][i];
        return f[x][0];
    }
    int siz[maxn],sum[maxn],ans;
    inline void dfs2(int x,int father)
    {
        siz[x]=sum[x];
        for (int i=head[x];i;i=Next[i])
        {
            int y=ver[i];
            if (y==father) continue;
            dfs2(y,x);
            siz[x]+=siz[y];
        }
        ans=max(ans,siz[x]);
    }
    int main()
    {
        int n,k;
        read(n);read(k);
        for (int i=1;i<n;++i)
        {
            int x,y;
            read(x);read(y);
            add(x,y);add(y,x);
        }
        dfs1(1,0,1);
        for (int i=1;i<=k;++i)
        {
            int a,b;
            read(a);read(b);
            int c=lca(a,b);
            ++sum[a],++sum[b],--sum[c];
            if (c!=1) --sum[fa[c]];
        }
        dfs2(1,0);
        printf("%d
    ",ans);
        return 0;
    }
    View Code

    第二题:POJ 3417

    题目大意:一棵有N个点的树,再往里面加入M条新边,现在要破坏其中的两条边,要求一条是原来树中的边,一条是新边,使其不连通。求方案的数量。

    1 ≤ N ≤ 100000), 1 ≤ M ≤ 100000)

    算法进阶好像也有;对于新加的一条边来说,肯定会与之前的树形成一个环,而此时环内的树上边和新加的这条边一同删除就会是一种方案。

    而这道题是将所有新边都加入后的情况,那么我们看每条边,如果没有与它形成环的情况,那么这条边删除肯定会使得图不连通,即情况就会加M,也就是和新加的M条边任意组合都可以。

    因而我们每次读入一条附加边,就给x到y的路径上的所有主要边记录上“被覆盖一次”,对于我们想要切割的一条主要边,有以下3种情况

    1 若这条边被覆盖0次,则可以任意再切断一条附加边。
    2 若这条边被覆盖1次,那么只能再切断唯一的一条附加边。
    3 若这条边被覆盖2次及以上,没有可行的方案;

    树上差分写一下;

    #include<algorithm>
    #include<bitset>
    #include<cctype>
    #include<cerrno>
    #include<clocale>
    #include<cmath>
    #include<complex>
    #include<cstdio>
    #include<cstdlib>
    #include<cstring>
    #include<ctime>
    #include<deque>
    #include<exception>
    #include<fstream>
    #include<functional>
    #include<limits>
    #include<list>
    #include<map>
    #include<iomanip>
    #include<ios>
    #include<iosfwd>
    #include<iostream>
    #include<istream>
    #include<ostream>
    #include<queue>
    #include<set>
    #include<sstream>
    #include<stack>
    #include<stdexcept>
    #include<streambuf>
    #include<string>
    #include<utility>
    #include<vector>
    #include<cwchar>
    #include<cwctype>
    using namespace std;
    const int maxn=1e5+10;
    template<typename T>inline void read(T &x)
    {
        x=0;
        T f=1,ch=getchar();
        while (!isdigit(ch)) ch=getchar();
        if (ch=='-') f=-1, ch=getchar();
        while (isdigit(ch)) x=(x<<1)+(x<<3)+(ch^48), ch=getchar();
        x*=f;
    }
    int ver[maxn<<1],Next[maxn<<1],head[maxn],len;
    inline void add(int x,int y)
    {
        ver[++len]=y,Next[len]=head[x],head[x]=len;
    }
    int fa[maxn],d[maxn],f[maxn][21];
    inline void dfs1(int x,int father,int deep)
    {
        fa[x]=father,d[x]=deep;
        for (int i=1;i<=20;++i)
            f[x][i]=f[f[x][i-1]][i-1];
        for (int i=head[x];i;i=Next[i])
        {
            int y=ver[i];
            if (y==father) continue;
            f[y][0]=x;
            dfs1(y,x,deep+1);
        }
    }
    inline int lca(int x,int y)
    {
        if (d[x]>d[y]) swap(x,y);
        for (int i=20;i>=0;--i)
            if (d[f[y][i]]>=d[x])
                y=f[y][i];
        if (x==y) return x;
        for (int i=20;i>=0;--i)
            if (f[x][i]!=f[y][i])
                x=f[x][i],y=f[y][i];
        return f[x][0];
    }
    int siz[maxn],sum[maxn],ans;
    inline void dfs2(int x,int father)
    {
        siz[x]=sum[x];
        for (int i=head[x];i;i=Next[i])
        {
            int y=ver[i];
            if (y==father) continue;
            dfs2(y,x);
            siz[x]+=siz[y];
        }
    }
    int main()
    {
        int n,m;
        read(n);read(m);
        for (int i=1;i<n;++i)
        {
            int x,y;
            read(x);read(y);
            add(x,y);add(y,x);
        }
        dfs1(1,0,1);
        for (int i=1;i<=m;++i)
        {
            int a,b;
            read(a);read(b);
            int c=lca(a,b);
            ++sum[a],++sum[b],sum[c]-=2;
        }
        dfs2(1,0);
        for (int i=1;i<=n;++i)
            if (!siz[i] && i!=1) ans+=m;
            else if (siz[i]==1) ++ans;
        printf("%d
    ",ans);
        return 0;
    }
    View ;

    第三题:LUOGU CF 739B

    CF 739B

    如果v可以控制u,那么从v到u的路上的所有结点都可以控制u,因为从v到u路上的dist(v, u)是递减的。

    可以每次遍历一个点的时候,二分找出根节点到当前点i路径上点,找出dist(j, i)刚好大于a[i]的点,树上差分统计这条路径。

    而后遍历当前点i的所有儿子结点k,cnt[i]+ = cnt[k]

    这里我写了倍增;

    #include<bits/stdc++.h>
    using namespace std;
    typedef long long ll;
    const int maxn=2e5+10;
    template<typename T>inline void read(T &x)
    {
        x=0;
        register int f=1;
        register char ch=getchar();
        while (!isdigit(ch)) { if (ch=='-') f=-1; ch=getchar();}
        while (isdigit(ch)) x=(x<<1)+(x<<3)+(ch^48), ch=getchar();
        x*=f;
    }
    template<typename T>inline void print(T x)
    {
        if (x<0) putchar('-'),x=-x;
        if (x>9) print(x/10);
        putchar(x%10+48);
    }
    int ver[maxn<<1],edge[maxn<<1],Next[maxn<<1],head[maxn],len;
    inline void add(int x,int y,int z)
    {
        ver[++len]=y,edge[len]=z,Next[len]=head[x],head[x]=len;
    }
    ll d[maxn];//必须用long long,否则过不了,如果你想一直莫名WA的话,可以不管
    int f[maxn][19];
    int a[maxn];
    int ans[maxn];
    inline void dfs(int x,int fa,int dist)
    {
        d[x]=d[fa]+dist,f[x][0]=fa;
        for (int i=1;i<=18;++i)
            f[x][i]=f[f[x][i-1]][i-1];
        for (int i=head[x];i;i=Next[i])
        {
            int y=ver[i],z=edge[i];
            if (y==fa) continue;
            dfs(y,x,z);
            register int val=a[y],k=1,now=y;
            while (k&&now)
            {
                if (d[now]-d[f[now][k]]<=val)
                {
                    val-=d[now]-d[f[now][k]];
                    now=f[now][k];
                    k<<=1;
                }
                else k>>=1;
            }
            if (d[now]-d[f[now][0]]<=val)
                now=f[now][0];
            --ans[f[now][0]],++ans[f[y][0]];
        }
        ans[fa]+=ans[x];
    }
    int main()
    {
        int n,y,z;read(n);
        for (int i=1;i<=n;++i)
            read(a[i]);
        for (int i=2;i<=n;++i)
        {
            read(y);read(z);
            add(i,y,z);add(y,i,z);
        }
        dfs(1,0,0);
        for (int i=1;i<=n;++i)
            print(ans[i]),putchar(' ');
        return 0;
    }
    View Code

    借教室:luoguP1083

    二分能满足的订单数。差分数组,对于二分的一个值,先差分到当前订单,扫描维护前缀和即为当前借的教室数与d作比较即可;

    #include <algorithm>
    #include <cctype>
    #include <cmath>
    #include <complex>
    #include <cstdio>
    #include <cstring>
    #include <deque>
    #include <functional>
    #include <list>
    #include <map>
    #include <iomanip>
    #include <iostream>
    #include <set>
    #include <queue>
    #include <stack>
    #include <string>
    #include <vector>
    #define sys system("PAUSE")
    using namespace std;
    const int maxn = 1e6 + 1000;
    typedef long long ull;
    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<<1)+(x<<3)+(ch^48);ch=getchar();}
        return x*f;
    }
    int n, m,a[maxn],d[maxn],r[maxn],l[maxn],c[maxn],sum[maxn],begin,end;
    inline bool check(int k) 
    {
        memset(c, 0, sizeof(c));
        for(int i=1;i<=k;++i) 
        {
            c[l[i]]+=d[i];
            c[r[i]+1]-=d[i];
        }
        for(int i=1;i<=n;++i)
        {
            sum[i]=sum[i-1]+c[i];
            if(sum[i]>a[i]) return false;
        }
        return true;
    }
    
    int main()
    {
        n=read(),m=read();
        for (int i=1;i<=n;++i) a[i]=read();
        for (int i=1;i<=m;++i) 
            d[i]=read(),l[i]=read(),r[i]=read();
        begin=1,end=m;
        if(check(m))
        {
            printf("0
    ");
            return 0;
        }
        while(begin<end)
        {
            int mid=(begin+end)>>1;
            if(check(mid)) begin=mid+1;
            else end=mid;
        }
        printf("%d
    %d
    ",-1,begin);
        return 0;
    }
    View Code

    好啦我要去学生物必修二的什么自由组合蒙圈题了,毕竟还有琵琶行等我背呢;

  • 相关阅读:
    通过JDBC连接oracle数据库的十大技巧
    asp.net里导出excel表方法汇总
    流式媒体
    html 另存为/打印/刷新/查看原文件等 代码
    JSP 语法详解
    让一个网页打开的同时自动为另一个网页自动填写表单并提交
    StreamWriter输出中文乱码的问题
    “mailto”的六则应用技巧
    返回目录大小
    JDBC基础(二)
  • 原文地址:https://www.cnblogs.com/Tyouchie/p/10388825.html
Copyright © 2011-2022 走看看