zoukankan      html  css  js  c++  java
  • 7.17 学习笔记

    7.17 学习笔记

    1 RMQ Range Mximum/Minimun Query

    区间最值问题。

    1.1 st 表

    (f_{i,j}) 表示从 (i) 开始,取 (2^j) 个数的最小值。形式化来说,取得是 ([i,i+2^j-1]) 的最小值。

    其实是一个倍增的过程。可以做到 (O(nlog n)) 预处理,(O(1)) 查询最值。

    同样运用倍增思想的还有快速幂。

    过于简单,不放代码。

    2 LCA

    2.1 倍增求 LCA

    我们只需要利用倍增的思想预处理出所有节点 (2) 的幂网上的父亲是谁,以及每个点的深度,然后往上跳就可以。

    树上倍增法同时也是我们在树上收集信息的常用方法。

    预处理复杂度 (O(nlog n)) 。询问复杂度 (O(log n))

    过于简单,不过代码。

    2.2 (O(1)) 求 LCA

    这里给出欧拉序的严格定义。如图:

    也就是说,我们 dfs 每次经过都要把该点加入欧拉序。我们发现每一个点加入的次数是所有儿子的个数加 (1)

    那么除了根节点不给其它节点做儿子外,其它节点都给别人做儿子,所以除了根节点,每一个点的贡献都是 (2)

    这个 (2) 的贡献,一个是给别人当儿子,另一个是其本身。

    容易证明,在欧拉序上,设 (x) 第一次出现的编号为 (x_0)(y) 第一次出现的编号为 (y_1) ,那么 (x,y) 的 lca 在欧拉序上应该是欧拉序上(x_0,y_0) 之间深度最小的点。证明这个东西只需要证明 lca 一定在这个区间并且在这个区间内不会出现比 lca 深度小或相等的点。这两者都容易证明。

    以前 (O(1)) lca 并没有打过,所以我们这里把代码写一下。

    不知道是不是我打的常数太大了,跑的没有树链剖分快,也许是洛谷模板题询问有点少。

    #include<bits/stdc++.h>
    #define dd double
    #define ld long double
    #define ll long long
    #define uint unsigned int
    #define ull unsigned long long
    #define N 1000010
    #define M 3000010
    using namespace std;
    
    const int INF=0x3f3f3f3f;
    
    template<typename T> inline void read(T &x) {
        x=0; int f=1;
        char c=getchar();
        for(;!isdigit(c);c=getchar()) if(c == '-') f=-f;
        for(;isdigit(c);c=getchar()) x=x*10+c-'0';
        x*=f;
    }
    
    template<typename T> inline T Min(T a,T b){
        return a<b?a:b;
    }
    
    struct edge{
        int to,next;
        inline void intt(int to_,int ne_){
            to=to_;next=ne_;
        }
    };
    edge li[M];
    int head[N],tail;
    
    inline void add(int from,int to){
        li[++tail].intt(to,head[from]);
        head[from]=tail;
    }
    
    struct point_deep{
        int id,deep;
        inline point_deep() {}
        inline point_deep(int id,int deep) : id(id),deep(deep) {}
        inline bool operator < (const point_deep &b) const{
            return deep<b.deep;
        }
    };
    point_deep xulie[N];
    
    int deep[N],tailx,FirApp[N];
    
    inline void dfs(int k,int fa){
        deep[k]=deep[fa]+1;
        xulie[++tailx]=point_deep(k,deep[k]);
        if(!FirApp[k]) FirApp[k]=tailx;
        for(int x=head[k];x;x=li[x].next){
            int to=li[x].to;
            if(to==fa) continue;
            dfs(to,k);
            xulie[++tailx]=point_deep(k,deep[k]);
        }
    }
    
    point_deep st[N][21];
    
    inline void build_st(){
        for(int i=1;i<=tailx;i++) st[i][0]=xulie[i];
        for(int i=1;i<=20;i++){
            for(int j=1;j+(1<<i)-1<=tailx;j++){
                st[j][i]=Min(st[j][i-1],st[j+(1<<(i-1))][i-1]);
            }
        }
    }
    
    inline point_deep query(int l,int r){
        int len=log2(r-l+1);
        return Min(st[l][len],st[r-(1<<len)+1][len]);
    }
    
    int n,m,s;
    
    int main(){
        read(n);read(m);read(s);
        for(int i=1;i<=n-1;i++){
            int from,to;read(from);read(to);
            add(from,to);add(to,from);
        }
        dfs(s,0);
        build_st();
        for(int i=1;i<=m;i++){
            int l,r;
            read(l);read(r);
            int fl=FirApp[l],fr=FirApp[r];
            if(fl>fr) swap(fl,fr);
            point_deep nowans=query(fl,fr);
            printf("%d
    ",nowans.id);
        }
        return 0;
    }
    

    2.3 LCA 的应用

    LCA 有很多应用,包括但不限于树上差分,求树上两点距离,轻重链剖分也需要用 lca。

    这些东西我好像都写过博客,所以这里不再赘述。

    3 二维 st 表

    对于一个二维数组有对应的二维 st 表。设 (f_{i,j,a,b}) 表示横坐标在 ([i,i+2^a-1]) 纵坐标在 ([j,j+2^b+1]) 之间的最值。

    转移也是类似的。我们直接按照二进制分割就可以。

    需要注意是否从 (0) 开始。

    过程具体来说,我们先预处理点的情况,再预处理一个长条的情况,最后处理变宽的情况。

    至于查询,思路大致和一维 st 表一样。

    代码:

    inline void build_st(){
        int maxx=Max(n,m);
        lg2[0]=-1;for(int i=1;i<=maxx;i++) lg2[i]=lg2[i/2]+1;
        for(int i=1;i<=n;i++) for(int j=1;j<=m;j++) st[i][j][0][0]=val[i][j];
        for(int i=1;i<=n;i++)
            for(int j=1;j<=lg2[m];j++)
                for(int k=1;k+(1<<j)-1<=m;k++){
                    st[i][k][0][j]=Max(st[i][k][0][j-1],st[i][k+(1<<(j-1))][0][j-1]);
                }
                    
        for(int i=1;i<=lg2[n];i++)
            for(int j=0;j<=lg2[m];j++)
                for(int k=1;k+(1<<i)-1<=n;k++)
                    for(int q=1;q+(1<<j)-1<=m;q++){
                        st[k][q][i][j]=Max(st[k][q][i-1][j],st[k+(1<<(i-1))][q][i-1][j]);
                    }
                        
    }
    
    inline int query(int sx,int sy,int xx,int xy){
        int len1=lg2[xx-sx+1],len2=lg2[xy-sy+1];
        int maxx=-INF;
        maxx=Max(maxx,Max(st[sx][sy][len1][len2],st[xx-(1<<len1)+1][xy-(1<<len2)+1][len1][len2]));
        maxx=Max(maxx,Max(st[xx-(1<<len1)+1][sy][len1][len2],st[sx][xy-(1<<len2)+1][len1][len2]));
        return maxx;
    }
    

    4 例题

    过水的题不整理。

    4.1 删数问题加强版

    链接

    我们考虑选首位,首先一定要让首位选最好的,在给定 (m) 的情况下,首位能选的位置是有限的,选最小的数,这是一个经典的 RMQ 问题,所以我们可以用 st 表做。接下来更新下一个的备选区间就可以。

    代码:

    #include<bits/stdc++.h>
    #define dd double
    #define ld long double
    #define ll long long
    #define uint unsigned int
    #define ull unsigned long long
    #define N 1010
    #define M number
    using namespace std;
    
    const int INF=0x3f3f3f3f;
    
    template<typename T> inline void read(T &x) {
        x=0; int f=1;
        char c=getchar();
        for(;!isdigit(c);c=getchar()) if(c == '-') f=-f;
        for(;isdigit(c);c=getchar()) x=x*10+c-'0';
        x*=f;
    }
    
    template<typename T> T Min(T a,T b){
        return a>b?b:a;
    }
    
    typedef pair<int,int> P;
    P st[N][10];
    
    int ans[N],tail,a[N],len,m,lg2[N];
    char s[N];
    
    inline void build_st(){
        memset(st,0,sizeof(st));
        lg2[0]=-1;for(int i=1;i<=len;i++) lg2[i]=lg2[i/2]+1;
        for(int i=1;i<=len;i++){
            st[i][0].first=a[i];st[i][0].second=i;
            // printf("i:%d j:0 st:%d %d
    ",i,st[i][0].first,st[i][0].second);
        }
        for(int i=1;i<=10;i++){
            for(int j=1;j+(1<<i)-1<=len;j++){
                st[j][i]=Min(st[j][i-1],st[j+(1<<(i-1))][i-1]);
                // printf("i:%d j:%d st:%d %d
    ",i,j,st[j][i].first,st[j][i].second);
            }
        }
    }
    
    inline P query(int l,int r){
        int lglen=lg2[r-l+1];
        return Min(st[l][lglen],st[r-(1<<lglen)+1][lglen]);
    }
    
    int main(){
        // freopen("my.in","r",stdin);
        // freopen("my.out","w",stdout);
        while(cin>>s>>m){
            tail=0;
            len=strlen(s);
            for(int i=1;i<=len;i++) a[i]=s[i-1]-'0';
            int l=1;m=len-m;build_st();
            for(int i=m;i>=1;i--){
                P now=query(l,len-m+1);
                ans[++tail]=now.first;
                l=now.second+1;m--;
            }
            int head=0;
            while(head+1<tail&&ans[head+1]==0) head++;
            for(int i=head+1;i<=tail;i++) printf("%d",ans[i]);
            putchar('
    ');
        }
    }
    

    4.2 Difference Is Beautiful

    链接

    首先发现答案有二分性,然后考虑以固定点为端点向右能够延伸的最大长度是多少,这个东西可以用双指针 (O(n)) 统计,我们考虑二分答案。设当前二分为 (mid) ,设当前询问区间为 (l,r) ,所以我们可以用 (st) 表维护长度最值,对于一个区间,我们只需要求 ([l,r-mid+1]) 的最值就可以了,看这个最值是否大于等于 (mid) ,为什么是 (r-mid+1) 是因为要保证区间长度至少要保证有 (mid)

    代码:

    #include<iostream>
    #include<cstdio>
    #include<algorithm>
    #include<cmath>
    #define dd double
    #define ld long double
    #define ll long long
    #define uint unsigned int
    #define ull unsigned long long
    #define N 200010
    #define M 2000010
    using namespace std;
    
    const int INF=0x3f3f3f3f;
    const int mod=1e6;
    
    template<typename T> inline void read(T &x) {
        x=0; int f=1;
        char c=getchar();
        for(;!isdigit(c);c=getchar()) if(c == '-') f=-f;
        for(;isdigit(c);c=getchar()) x=x*10+c-'0';
        x*=f;
    }
    
    template<typename T> inline void write(T a){
        if(a<0){putchar('-');a=-a;}
        if(!a) return;
        write(a/10);putchar(a%10+'0');
    }
    
    template<typename T> inline T Max(T a,T b){
        return a<b?b:a;
    }
    
    int n,m,a[N],cnt[M],b[N],log_2[N];
    int st[N][21];
    // build
    inline void build_st(){
        log_2[0]=-1;for(int i=1;i<=n;i++) log_2[i]=log_2[i/2]+1;
        for(int i=1;i<=n;i++) st[i][0]=b[i];
        for(int i=1;i<=log_2[n];i++){
            for(int j=1;j+(1<<i)-1<=n;j++){
                st[j][i]=Max(st[j][i-1],st[j+(1<<(i-1))][i-1]);
            }
        }
    }
    
    inline int query(int l,int r){
        int len=log_2[r-l+1];
        return Max(st[l][len],st[r-(1<<len)+1][len]);
    }
    
    inline bool check(int z,int y,int mid){
        if(mid>(y-z+1)) return 0;
        int res=query(z,y-mid+1);
        if(res>=mid) return 1;
        else return 0;
    }
    
    inline int erfen(int z,int y){
        int l=1,r=n,ans;
        while(l<=r){
            int mid=(l+r)>>1;
            if(check(z,y,mid)) ans=mid,l=mid+1;
            else r=mid-1;
        }
        return ans;
    }
    
    int main(){
        read(n);read(m);
        for(int i=1;i<=n;i++){read(a[i]);a[i]+=mod;}
        for(int l=1,r=0;l<=n;l++){
            while(r+1<=n&&cnt[a[r+1]]<=0){r++;cnt[a[r]]++;}
            b[l]=r-l+1;cnt[a[l]]--;
        }
        build_st();
        for(int i=1;i<=m;i++){
            int z,y;read(z);read(y);z++;y++;
            int ans=erfen(z,y);
            printf("%d
    ",ans);
        }
        return 0;
    }
    

    4.3 CF1301E Nanosoft

    链接

    这个题竟然没有人用二维 st 表过,这里写个题解。

    以下,我们称一个 logo 最中间的那个点(不是方块)为关键点。大致思路如下:

    1. 我们预处理出每一个点作为关键点时,最大方块边长除以 (2) 为多少。这个可以二分。
    2. 对于每一个询问,答案显然具有二分性,所以我们可以二分来做。

    因为我们在第二次二分中需要 (O(1)) 判断,所以我们需要提前预处理出二维 st 表。

    第一次二分中的判断我们可以用二维前缀和优化到 (O(1))

    代码:

    #include<bits/stdc++.h>
    #define dd double
    #define ld long double
    #define ll long long
    #define uint unsigned int
    #define ull unsigned long long
    #define N 501
    #define M number
    using namespace std;
    
    const int INF=0x3f3f3f3f;
    
    template<typename T> inline void read(T &x) {
        x=0; int f=1;
        char c=getchar();
        for(;!isdigit(c);c=getchar()) if(c == '-') f=-f;
        for(;isdigit(c);c=getchar()) x=x*10+c-'0';
        x*=f;
    }
    
    template<typename T> T Min(T a,T b){
        return a<b?a:b;
    }
    
    template<typename T> T Max(T a,T b){
        return a<b?b:a;
    }
    
    char s[N][N];
    int sum[4][N][N],st[N][N][10][10],val[N][N],lg2[N],n,m,q;
    //sum: 0 is red, 1 is green, 2 is blue, 3 is yellow
    //st is the maxest
    
    inline void build_st(){
        int maxx=Max(n,m);
        lg2[0]=-1;for(int i=1;i<=maxx;i++) lg2[i]=lg2[i/2]+1;
        for(int i=1;i<=n;i++) for(int j=1;j<=m;j++) st[i][j][0][0]=val[i][j];
        for(int i=1;i<=n;i++)
            for(int j=1;j<=lg2[m];j++)
                for(int k=1;k+(1<<j)-1<=m;k++){
                    st[i][k][0][j]=Max(st[i][k][0][j-1],st[i][k+(1<<(j-1))][0][j-1]);
                }
                    
        for(int i=1;i<=lg2[n];i++)
            for(int j=0;j<=lg2[m];j++)
                for(int k=1;k+(1<<i)-1<=n;k++)
                    for(int q=1;q+(1<<j)-1<=m;q++){
                        st[k][q][i][j]=Max(st[k][q][i-1][j],st[k+(1<<(i-1))][q][i-1][j]);
                    }
                        
    }
    
    inline int query(int sx,int sy,int xx,int xy){
        int len1=lg2[xx-sx+1],len2=lg2[xy-sy+1];
        int maxx=-INF;
        maxx=Max(maxx,Max(st[sx][sy][len1][len2],st[xx-(1<<len1)+1][xy-(1<<len2)+1][len1][len2]));
        maxx=Max(maxx,Max(st[xx-(1<<len1)+1][sy][len1][len2],st[sx][xy-(1<<len2)+1][len1][len2]));
        return maxx;
    }
    
    inline bool check(int x,int y,int mid){
        int dx,dy,lx=x,ly=y;
        x=lx;y=ly;dx=x-mid,dy=y-mid;
        if(sum[0][x][y]+sum[0][dx][dy]-sum[0][x][dy]-sum[0][dx][y]!=mid*mid) return 0;
        x=lx+mid;y=ly;dx=x-mid;dy=y-mid;
        if(sum[3][x][y]+sum[3][dx][dy]-sum[3][x][dy]-sum[3][dx][y]!=mid*mid) return 0;
        x=lx;y=ly+mid;dx=x-mid;dy=y-mid;
        if(sum[1][x][y]+sum[1][dx][dy]-sum[1][x][dy]-sum[1][dx][y]!=mid*mid) return 0;
        x=lx+mid;y=ly+mid;dx=x-mid;dy=y-mid;
        if(sum[2][x][y]+sum[2][dx][dy]-sum[2][x][dy]-sum[2][dx][y]!=mid*mid) return 0;
        return 1;
    }
    
    inline int erfen(int x,int y){
        int l=0,r,res;
        r=Min(Min(x,y),Min(n-x,m-y));
        while(l<=r){
            int mid=(l+r)>>1;
            if(check(x,y,mid)){res=mid;l=mid+1;}
            else r=mid-1;
        }
        return res;
    }
    
    inline bool AnsCheck(int sx,int sy,int xx,int xy,int mid){
        sx+=mid;sy+=mid;xx-=mid;xy-=mid;
        if(xx<sx||xy<sy) return 0;
        int maxx=query(sx,sy,xx,xy);
        if(maxx>=mid) return 1;
        return 0;
    }
    
    inline int BinaryAns(int sx,int sy,int xx,int xy){
        int l=0,r,res;
        r=Min(xx-sx,xy-sy);if(r&1) r--;
        while(l<=r){
            int mid=(l+r)>>1;
            if(AnsCheck(sx,sy,xx,xy,mid)){res=mid;l=mid+1;}
            else r=mid-1;
        }
        return res;
    }
    
    int main(){
        // freopen("my.in","r",stdin);
        // freopen("my.out","w",stdout);
    
        read(n);read(m);read(q);
        for(int i=1;i<=n;i++) scanf("%s",s[i]+1);
        for(int i=1;i<=n;i++)
            for(int j=1;j<=m;j++){
                if(s[i][j]=='R') sum[0][i][j]=1;
                else if(s[i][j]=='G') sum[1][i][j]=1;
                else if(s[i][j]=='B') sum[2][i][j]=1;
                else if(s[i][j]=='Y') sum[3][i][j]=1;
            }
        
        for(int i=1;i<=n;i++)
            for(int j=1;j<=m;j++)
                for(int k=0;k<=3;k++)
                    sum[k][i][j]+=sum[k][i][j-1]+sum[k][i-1][j]-sum[k][i-1][j-1];
        
        for(int i=1;i<=n+1;i++)
            for(int j=1;j<=m+1;j++){
                val[i][j]=erfen(i-1,j-1);
            }
        n++;m++;
    
        // for(int i=1;i<=n;i++)
        //     for(int j=1;j<=m;j++)
        //         printf("i:%d j:%d val:%d
    ",i,j,val[i][j]);
    
        build_st();
        while(q--){
            int sx,sy,xx,xy;read(sx);read(sy);read(xx);read(xy);xx++;xy++;
            int ans=BinaryAns(sx,sy,xx,xy);
            printf("%d
    ",ans*ans*4);
        }
        return 0;
    }
    

    4.4 UVA1707 Surveillance

    链接

    遇到环上问题,我们先不考虑环,先考虑链。如果是链的话,我们直接预处理出把每个位置覆盖住的线段最右端点是哪个位置,然后贪心就可以了。这个做法 (O(n))

    我们考虑断环为链,复制一段接在尾端,如果我们直接枚举左端点暴力做的话是 (O(n^2)) ,考虑用倍增优化这个过程,预处理出走 (2^i) 步能走到的位置,就可以做了。时间复杂度 (O(nlog n))

    代码:

    #include<bits/stdc++.h>
    #define dd double
    #define ld long double
    #define ll long long
    #define uint unsigned int
    #define ull unsigned long long
    #define N 2000010
    #define M number
    using namespace std;
    
    const int INF=0x3f3f3f3f;
    
    template<typename T> inline void read(T &x) {
        x=0; int f=1;
        char c=getchar();
        for(;!isdigit(c);c=getchar()) if(c == '-') f=-f;
        for(;isdigit(c);c=getchar()) x=x*10+c-'0';
        x*=f;
    }
    
    template<typename T> T Max(T a,T b){
        return a<b?b:a;
    }
    
    template<typename T> T Min(T a,T b){
        return a<b?a:b;
    }
    
    int n,k,a[N],nxt[N],nx[N][21],lg2[N],ans=INF;
    
    inline void clear(){
        for(int i=1;i<=2*n;i++) nxt[i]=a[i]=0;ans=INF;
        memset(nx,0,sizeof(nx));
    }
    
    int main(){
        // freopen("my.in","r",stdin);
        // freopen("my.out","w",stdout);
        lg2[0]=-1;for(int i=1;i<=2*1e6;i++) lg2[i]=lg2[i/2]+1;
        while(cin>>n>>k){
            for(int i=1;i<=k;i++){
                int l,r;read(l);read(r);
                if(r<l) r+=n;
                a[l]=Max(a[l],r);
            }
            int maxr=0;
            for(int i=1;i<=2*n;i++){
                if(a[i]){
                    if(nxt[i-1]!=INF) nxt[i]=Max(nxt[i-1],a[i]);
                    else nxt[i]=a[i];
                    maxr=Max(maxr,a[i]);
                }
                else if(maxr>=i) nxt[i]=nxt[i-1];
                else nxt[i]=INF;
                nx[i][0]=nxt[i]+1;
            }
            for(int i=1;i<=lg2[2*n];i++){
                for(int j=1;j<=2*n;j++){
                    if(nx[j][i-1]>=INF){
                        nx[j][i]=INF;
                    }
                    else nx[j][i]=nx[nx[j][i-1]][i-1];
                }
            }
            for(int i=1;i<=n;i++){
                int now=i,cnt=0;
                for(int j=lg2[2*n];j>=0;j--){
                    if(nx[now][j]<i+n){
                        now=nx[now][j];
                        cnt+=(1<<j);
                    }
                }
                if(nx[now][0]<=INF){ans=Min(ans,cnt+1);}
            }
            if(ans!=INF) printf("%d
    ",ans);
            else printf("impossible
    ");
            clear();
        }
        return 0;
    }
    

    4.5 CF609E Minimum spanning tree for each edge

    链接

    我们考虑先求出最小生成树,设边权和为 (sum)

    对于一条边,如果他在最小生成树上,那么就可以不做任何操作。

    否则,我们在树上找到这两个点路径上的最大值,建取最大值,然后把我们当前的这条边加进去。

    上面这个贪心的想法正确性显然,因为我们要保证权值尽量小。

    至于两个点路径上的最大值,我们可以用树上倍增法来预处理。

    在与处理时注意特判:

    代码:

    #include<bits/stdc++.h>
    #define dd double
    #define ld long double
    #define ll long long
    #define int long long
    #define uint unsigned int
    #define ull unsigned long long
    #define N 200010
    #define M 200010
    using namespace std;
    
    const int INF=0x3f3f3f3f;
    
    template<typename T> inline void read(T &x) {
        x=0; int f=1;
        char c=getchar();
        for(;!isdigit(c);c=getchar()) if(c == '-') f=-f;
        for(;isdigit(c);c=getchar()) x=x*10+c-'0';
        x*=f;
    }
    
    template<typename T> inline T Max(T a,T b){
        return a<b?b:a;
    }
    
    struct UnionFindSet{
        int fa[N];
        inline void init(int n){
            for(int i=1;i<=n;i++) fa[i]=i;
        }
        inline int find(int x){
            return fa[x]==x?x:fa[x]=find(fa[x]);
        }
        inline bool Merge_IsTheSame(int a,int b){
            int faa=find(a),fab=find(b);
            if(faa==fab) return 1;
            fa[faa]=fab;return 0;
        }
    };
    UnionFindSet ufs;
    
    struct Bian{
        int from,to,w,id;
        inline bool operator < (const Bian &b)const{
            return w<b.w;
        }
    };
    Bian bian[M];
    
    bool IsChoose[M];
    
    struct edge{
        int to,next,w;
        inline void intt(int to_,int ne_,int w_){
            to=to_;next=ne_;w=w_;
        }
    };
    edge li[M<<1];
    int head[N],tail;
    
    inline void add(int from,int to,int w){
        li[++tail].intt(to,head[from],w);
        head[from]=tail;
    }
    
    int n,m;
    
    inline int Kruscal(){
        ufs.init(n);
        sort(bian+1,bian+m+1);
        int cnt=0,nowans=0;
        for(int i=1;i<=m;i++){
            if(!ufs.Merge_IsTheSame(bian[i].from,bian[i].to)){
                IsChoose[i]=1;nowans+=bian[i].w;
                add(bian[i].from,bian[i].to,bian[i].w);add(bian[i].to,bian[i].from,bian[i].w);
            }
            if(tail==2*n-2) break;
        }
        return nowans;
    }
    
    int ans[M];
    
    int fa[N][21],MaxEdge[N][21],deep[N];
    
    inline void dfs(int k,int fat){
        fa[k][0]=fat;for(int i=1;i<=20;i++) fa[k][i]=fa[fa[k][i-1]][i-1];
        deep[k]=deep[fat]+1;
        for(int i=1;i<=20;i++) MaxEdge[k][i]=Max(MaxEdge[k][i-1],MaxEdge[fa[k][i-1]][i-1]);
        for(int x=head[k];x;x=li[x].next){
            int to=li[x].to,w=li[x].w;
            if(to==fat) continue;
            MaxEdge[to][0]=w;dfs(to,k);
        }
    }
    
    inline int GetMax(int a,int b){
        int maxx=-INF;
        if(deep[a]<deep[b]) swap(a,b);
        for(int i=20;i>=0;i--)
            if(deep[fa[a][i]]>=deep[b]){maxx=Max(maxx,MaxEdge[a][i]);a=fa[a][i];}
        for(int i=20;i>=0;i--)
            if(fa[a][i]!=fa[b][i]){
                maxx=Max(maxx,Max(MaxEdge[a][i],MaxEdge[b][i]));a=fa[a][i];b=fa[b][i];
            }
        if(a!=b) maxx=Max(MaxEdge[a][0],Max(maxx,MaxEdge[b][0]));
        return maxx;
    }
    
    signed main(){
        read(n);read(m);
        for(int i=1;i<=m;i++){
            read(bian[i].from);read(bian[i].to);read(bian[i].w);bian[i].id=i;
        }
        int nowans=Kruscal();dfs(1,0);
        for(int i=1;i<=m;i++){
            if(IsChoose[i]){ans[bian[i].id]=nowans;continue;}
            int now=GetMax(bian[i].from,bian[i].to);
            ans[bian[i].id]=nowans-now+bian[i].w;
        }
        for(int i=1;i<=m;i++) printf("%lld
    ",ans[i]);
        return 0;
    }
    

    4.6 MEX Tree

    链接

    我们考虑钦定这棵树以 (0) 为根,然后手算出 MEX 为 (0,1) 时的答案。其余答案一定经过根节点,我们维护这条路径就可以。

    分类讨论细节比较多,是一道好题。

    #include<bits/stdc++.h>
    #define dd double
    #define ld long double
    #define ll long long
    #define int long long
    #define uint unsigned int
    #define ull unsigned long long
    #define N 400010
    #define M number
    using namespace std;
    
    const int INF=0x3f3f3f3f;
    
    template<typename T> inline void read(T &x) {
        x=0; int f=1;
        char c=getchar();
        for(;!isdigit(c);c=getchar()) if(c == '-') f=-f;
        for(;isdigit(c);c=getchar()) x=x*10+c-'0';
        x*=f;
    }
    
    template<typename T> T Min(T a,T b){
        return a<b?a:b;
    }
    
    template<typename T> T Max(T a,T b){
        return a<b?b:a;
    }
    
    typedef pair<int,int> P;
    
    struct edge{
        int to,next;
        inline void intt(int to_,int ne_){
            to=to_;next=ne_;
        }
    };
    edge li[N<<1];
    int head[N],tail;
    
    inline void add(int from,int to){
        li[++tail].intt(to,head[from]);
        head[from]=tail;
    }
    
    int t,n,siz[N],ans[N],deep[N],tailx,lg2[N],FirApp[N],x=1,y=1,spe;
    P xulie[N<<1],st[N][21];
    
    inline void dfs(int k,int fa){
        siz[k]=1;deep[k]=deep[fa]+1;xulie[++tailx]=make_pair(deep[k],k);FirApp[k]=tailx;
        for(int x=head[k];x;x=li[x].next){
            int to=li[x].to;
            if(to==fa) continue;
            dfs(to,k);
            siz[k]+=siz[to];xulie[++tailx]=make_pair(deep[k],k);
        }
    }
    
    inline void build_st(){
        lg2[0]=-1;for(int i=1;i<=tailx;i++) lg2[i]=lg2[i/2]+1;
        for(int i=1;i<=tailx;i++) st[i][0]=xulie[i];
        for(int i=1;i<=lg2[tailx];i++){
            for(int j=1;j+(1<<i)-1<=tailx;j++){
                st[j][i]=Min(st[j][i-1],st[j+(1<<(i-1))][i-1]);
            }
        }
    }
    
    inline P query(int l,int r){
        int len=lg2[r-l+1];
        return Min(st[l][len],st[r-(1<<len)+1][len]);
    }
    
    inline int GetLca(int a,int b){
        P res=query(Min(FirApp[a],FirApp[b]),Max(FirApp[a],FirApp[b]));
        return res.second;
    }
    
    inline void clear(){
        for(int i=1;i<=n+1;i++) ans[i]=head[i]=0;
        tail=0;x=y=1;tailx=0;
    }
    
    inline void prework(){
        ans[2]+=(1ll*(siz[1]-siz[2])*(siz[1]-siz[2]-1)/2);
        for(int x=head[1];x;x=li[x].next){
            int to=li[x].to;
            ans[1]+=(1ll*siz[to]*(siz[to]-1)/2);
            int lca=GetLca(2,to);
            if(lca==to) ans[2]-=(1ll*(siz[to]-siz[2])*(siz[to]-siz[2]-1)/2);
            else ans[2]-=1ll*siz[to]*(siz[to]-1)/2;
        }
    }
    
    inline bool insert(int k){
        int lca1=GetLca(k,x),lca2=GetLca(k,y);
        if(y!=1){
            if(lca1==k||lca2==k) return 1;
            else if(lca1==x) x=k;
            else if(lca2==y) y=k;
            else return 0;
        }
        else{
            if(lca1==k) return 1;
            else if(lca1==x) x=k;
            else if(lca1==1) y=k;
            else return 0;
        }
        if(deep[x]<deep[y]) swap(x,y);
        return 1;
    }
    // mex is k
    inline int GetAns(int k){
        int lca1=GetLca(k,x),lca2=GetLca(k,y);
        int res1,res2;
        if(y!=1){
            res1=siz[x];res2=siz[y];
            if(lca1==k||lca2==k) return 0;
            else if(lca1==x) res1-=siz[k];
            else if(lca2==y) res2-=siz[k];
        }
        else{
            res1=siz[x];res2=siz[y]-spe;
            if(lca1==k||lca2==k) return 0;
            else if(lca1==x) res1-=siz[k];
            else if(lca1==1) res2-=siz[k];
        }
        return res1*res2;
    }
    
    signed main(){
        read(t);
        while(t--){
            read(n);
            for(int i=1;i<=n-1;i++){
                int from,to;read(from);read(to);from++;to++;
                add(from,to);add(to,from);
            }
            dfs(1,0);build_st();
            prework();// already solve ans[1] ans[2]
            for(int i=3;i<=n+1;i++){
                if(!insert(i-1)) break;
                if(i==n+1){ans[i]=1;break;}
                if(i==3){
                    for(int x=head[1];x;x=li[x].next){
                        int to=li[x].to;
                        int lca=GetLca(to,2);
                        if(lca==to){spe=siz[to];break;}
                    }
                }
                ans[i]=GetAns(i);
            }
            for(int i=1;i<=n+1;i++) printf("%lld ",ans[i]);
            if(t) putchar('
    ');
            clear();
        }
        return 0;
    }//
    
  • 相关阅读:
    【转】Android中自动连接到指定SSID的Wi-Fi
    【转】多文件目录下makefile文件递归执行编译所有c文件
    Android NDK R9d 安装
    【转】第一个MiniGUI程序:模仿QQ界面
    FFmpeg缩放swscale详解 <转>
    【转】基于Qt, TUIO和TSLIB的嵌入式Linux下的多点触摸设计
    【转】TI-Davinci开发系列之六CCS5.2调试Linux内核
    【转】ffserver用法小结
    【转】Android HAL实例解析
    【转】DM8168图像处理Link
  • 原文地址:https://www.cnblogs.com/TianMeng-hyl/p/15025786.html
Copyright © 2011-2022 走看看