zoukankan      html  css  js  c++  java
  • Training for 分块&莫队

    分块 Study data Link:http://hzwer.com/8053.html    // hzwer讲的很好很全

    树上莫队Study Link:http://codeforces.com/blog/entry/43230   ( ery nice  并且有题目推荐

    莫队算法时间复杂度O( n * sqrt(n) )证明:

    由于每一块的大小为sqrt(n),故有sqrt(n)块,按照所属块为first key ,右端点为second key,考虑每一块,因为每块的右端点是Increase,故右端点最大的移动为N ,左端点的最大移动为 k × sqrt(n),故总复杂度为O(n×sqrt(n) + m×sqrt(n))  ≈  O( n * sqrt(n) )


    一道很牛逼的题 

    题意

    给一个n的数的序列,现有q次询问, 1代表是修改操作,2代表是查询操作,对于每次查询输出[1,1e6]以内出现偶数次最小的树(没有出现也算是偶数次)   (a[i],n,q<=1e6)

    分析

    对[1,1e6]分块,每次修改之前的数所在的块和修改后所在的块即可

    代码:待补


    CDOJ 1324

    分析

    简单的单点更新,区间最大值

    时间复杂度( n×sqrt(n) )

    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #include<cmath>
    #include<vector>
    #define ll long long
    using namespace std;
    const int maxn = 1e5+7;
    
    //blcok:每块的大小
    //num:块的数量
    //l[]:每块的左边界,r[]:每块的右边界
    //belong[]:每个位置的数属于那一块
    int n,q;
    int belong[maxn],block,l[maxn],r[maxn],num;
    ll a[maxn],Max[maxn];
    
    void build(){
        block=sqrt(n);
        num=n/block; if(n%num) num++;
        for(int i=1;i<=num;i++)
            l[i]=(i-1)*block, r[i]=i*block; // 处理出每块的左右边界
        r[num]=n;
        for(int i=1;i<=n;i++)
            belong[i]=(i-1)/block+1; // 每个位置的数属于那一块
        for(int i=1;i<=num;i++)
            for(int j=l[i];j<=r[i];j++)
              Max[i]=max(Max[i],a[j]);
    }
    
    void update(int x,int y){
        a[x]+=y;
        Max[belong[x]]=max(Max[belong[x]],a[x]);
    }
    
    ll ask(int x,int y)
    {
        ll ans=0;
        if(belong[x]==belong[y]){
            for(int i=x;i<=y;i++)
                ans=max(ans,a[i]);
        }
        else {
            for(int i=x;i<=r[belong[x]];i++)
                ans=max(ans,a[i]);
            for(int i=belong[x]+1;i<=belong[y]-1;i++)
                ans=max(ans,Max[i]);
            for(int i=l[belong[y]];i<=y;i++)
                ans=max(ans,a[i]);
        }
        return ans;
    }
    
    int main()
    {
        scanf("%d%d", &n, &q);
        build();
        for(int i=1;i<=q;i++)
        {
            int op,l,r;
            scanf("%d%d%d", &op, &l, &r);
            if(op==1) update(l,r);
            else
                printf("%lld
    ", ask(l,r));
        }
        return 0;
    }

    莫队入门

    codeforces 617E. XOR and Favorite Number

    题意

    给你一个n个数的序列,有m次询问,每次询问问你 L,R, K,区间[l,r]  ,有多少对(i,j)( l ≤ i ≤ j ≤ r )使得  the xor of the numbers ai, ai + 1, ..., aj is equal to k.

     (1 ≤ n, m ≤ 1e5, 0 ≤ k ≤ 1e6,0 ≤ ai ≤ 1e6) 

    分析

    由于xor具有 A^A=0性质,处理出xor前缀,区间[L,R]的xor值即为pre[R]^pre[L-1],直接上莫队,一边更新区间[L,R]每个异或值出现的次数,统计时每次只需要询问修改的区间更新(加/减)即可

     trick:需要注意更新的先后顺序以及处理[L,R]区间,由于xor的特殊性,需要处理L-1  !!!

    #include<bits/stdc++.h>
    #define ll long long
    using namespace std;
    const int maxn = 3e6 +7;
    
    int belong[maxn],a[maxn],num,block, ans[maxn];
    ll f[maxn];
    int n,m,k;
    
    struct node
    {
        int l,r,id;
        friend bool operator < (node a,node b)
        {
            if(belong[a.l] == belong[b.l])
                return a.r<b.r;
            return belong[a.l] < belong[b.l];
        }
    }q[maxn];
    
    
    ll Ans=0;
    
    void del(int p)   // 因为删除的这个位置不在考虑的区间内,所以先更新ans,在查询
    {
        ans[a[p]]--;
        Ans-=ans[a[p]^k];
    }
    
    void add(int p)   // 因为加的这个数如果恰好为L-1,这个应不作为考虑,不是L-1的话,这个位置会在下次统计上,所以先查询后更新
    {
        Ans+=ans[a[p]^k];
        ans[a[p]]++;
    }
    
    void solve()
    {
        int L=1,R=0;
        for(int i=1;i<=m;i++)
        {
            while(L<q[i].l)  // 由于要得到A[l-1]^A[r] , 故应该先更新,再L++;
            {
                del(L-1);
                L++;
            }
            while(L>q[i].l){ //当L>q[i].l ,为了便于思考取L=q[i].l+1,其实已经处理了q[i].l这个位置,因为上次统计的时候利用了这个位置
                L--;
                add(L-1);
            }
            while(R<q[i].r){ // 右端点没有什么特殊的
                R++;
                add(R);
            }
            while(R>q[i].r){
                del(R);
                R--;
            }
            f[q[i].id]=Ans;
        }
    }
    
    int main()
    {
        ans[0]=1;       // 注意什么都不取得时候也有一种情况,当A[t]^K=0时,也有一种情况,L=0时
        scanf("%d%d%d", &n, &m, &k);
        block=int(sqrt(n));
        for(int i=1;i<=n;i++){
            scanf("%d", &a[i]);
            a[i]^=a[i-1];
            belong[i]=(i-1)/block+1;
        }
        for(int i=1;i<=m;i++){
            scanf("%d%d", &q[i].l, &q[i].r);
            q[i].id=i;
        }
        sort(q+1,q+m+1);
        solve();
        for(int i=1;i<=m;i++)
            printf("%lld
    ", f[i]);
        return 0;
    }
    

    bzoj2038 小z的袜子(hose)

    分析

    莫队板子题

    #include<bits/stdc++.h>
    #define ll long long
    using namespace std;
    const int maxn = 1e6+7;
    
    int belong[maxn],a[maxn],num,block, ans[maxn];
    ll fz[maxn],fm[maxn];
    int n,m,k;
    
    struct node{
        int l,r,id;
        friend bool operator < (node a,node b)
        {
            if(belong[a.l] == belong[b.l])
                return a.r<b.r;
            return belong[a.l] < belong[b.l];
        }
    }q[maxn];
    
    ll gcd(ll a, ll b)
    {
        return b==0? a : gcd(b, a%b);
    }
    
    
    ll Ans=0;
    
    void del(int p)
    {
       Ans-=ans[a[p]]-1;
       ans[a[p]]--;
    }
    
    void add(int p)
    {
        ans[a[p]]++;
        Ans+=ans[a[p]]-1;
    }
    
    void solve()
    {
       int L=1,R=0;
       for(int i=1;i<=m;i++)
       {
           while(L<q[i].l)
            {
               del(L);
               L++;
           }
           while(L>q[i].l)
           {
               L--;
               add(L);
           }
           while(R<q[i].r)
           {
               R++;
               add(R);
           }
           while(R>q[i].r)
           {
               del(R);
               R--;
           }
           ll k=((1LL)*(q[i].r-q[i].l+1))*(q[i].r-q[i].l)/2;
           if(Ans!=0)
           {
               ll g=gcd(Ans,k);
               fz[q[i].id]=Ans/g;
               fm[q[i].id]=k/g;
           }
           else
           {
               fz[q[i].id]=Ans;
               fm[q[i].id]=1;
           }
       }
    }
    
    int main()
    {
        scanf("%d%d", &n, &m, &k);
        block=int(sqrt(n));
        for(int i=1;i<=n;i++){
            scanf("%d", &a[i]);
            belong[i]=(i-1)/block+1;
        }
        for(int i=1;i<=m;i++){
            scanf("%d%d", &q[i].l, &q[i].r);
            q[i].id=i;
        }
        sort(q+1,q+m+1);
        solve();
        for(int i=1;i<=m;i++)
            printf("%lld/%lld
    ", fz[i],fm[i]);
        return 0;
    }

    codeforces 620 F. Xors on Segments

    题意

    给一个n个数的序列,给出m个询问 l, r, 问从区间[L,R]中选两个数 a,b(a<=b,可以同一个数),f=a^(a+1)....^(b-1)^b 的最大值(1 ≤ n ≤ 5e4,  1 ≤ m ≤ 5e3 ,1 ≤ ai ≤ 1e6,1<=L<=R<=n

    分析

    数据水了放过了n^2,但数组开太大也会T,正解:莫队+Tire

    solution:暴力的复杂度是(m×n^2)肯定gg,那么考虑优化,对所有的询问离线,dp[i]: 以a[i]作为开头的元素的最大值,询问所有 j > i,当以 j 为右端点的询问的左端点<= i时更新

    时间复杂度:O(n^2 + n×m)

    #include<bits/stdc++.h>
    #define ll long long
    using namespace std;
    const int maxn = 5e4+7;
    
    int a[maxn],pre[1000007],n,m;
    vector<pair<int,int> >v[maxn];
    int ans[maxn];
    
    int main()
    {
        for(int i=1;i<1000007;i++)
            pre[i]=pre[i-1]^i;
        scanf("%d%d",&n, &m);
        for(int i=1;i<=n;i++)
        {
            scanf("%d",&a[i]);
        }
        int l,r;
        for(int i=1;i<=m;i++)
        {
            scanf("%d%d", &l, &r);
            v[r].push_back({l,i});
        }
        int dp;
        for(int i=1;i<=n;i++)
        {
            dp=0;
            for(int j=i;j<=n;j++)
            {
                dp=max(dp, pre[a[j]] ^ pre[a[i]] ^ min(a[i],a[j]));
                for(auto it : v[j])
                {
                    if(it.first<=i)  ans[it.second]=max(ans[it.second],dp);
                }
            }
        }
        for(int i=1;i<=m;i++)
        {
            printf("%d
    ",ans[i]);
        }
        return 0;
    }

    BZOJ 1086 糖果公园   DFS+贪心

    分析

    dfs的时候统计子树叶子数量,大于等于b时归为一个省,省会就为当前父节点,怎么把子树节点保存下来呢?用一个栈即可。最后剩下小于b直接给最后一个省即可

    #include<iostream>
    #include<cstdio>
    using namespace std;
    
    
    const int maxn = 1000+2;
    
    int belong[maxn],q[maxn],top,sz[maxn],cap[maxn],sum;
    int head[maxn],nxt[maxn*2],to[maxn*2],tot,k;
    int n,b;
    
    void addedge(int u,int v)
    {
        to[++tot]=v;
        nxt[tot]=head[u];
        head[u]=tot;
    }
    
    void dfs(int x,int fa)
    {
        q[++top]=x;
        for(int i=head[x];i;i=nxt[i])
        {
            int v=to[i];
            if(v!=fa)
            {
                dfs(v,x);
                if(sz[x]+sz[v]>=b)
                {
                    sz[x]=0;
                    cap[++sum]=x;
                    //cout<<v<<' '<<x<<' '<<sum<<endl;
                    while(q[top]!=x)
                    {
                        belong[q[top]]=sum;
                        --top;
                    }
                }
                else
                    sz[x]+=sz[v];
            }
        }
        sz[x]++;
    }
    
    int main()
    {
        scanf("%d%d", &n, &b);
        if(n<b){puts("0");return 0;}
        int u,v;
        for(int i=1;i<=n-1;i++){
            scanf("%d%d", &u, &v);
            addedge(u,v);
            addedge(v,u);
        }
        dfs(1,0);
        if(n==1)
            cout<<1<<endl<<1<<endl<<1<<endl;
        else
        {
            printf("%d
    ",sum);
            for(int i=1;i<=n;i++)
            {
                if(!belong[i])
                    belong[i]=sum;
            }
            for(int i=1;i<=n;i++)
                printf("%d%c", belong[i], i==n ? '
    ':' ');
            for(int i=1;i<=sum;i++)
                printf("%d%c", cap[i], i==n?'
    ':' ');
        }
        return 0;
    }

    Codeforces 369E

    题意

    在x轴上,给n个区间 [ Li, Ri ],m个询问,每个询问包含k个点,对于每个询问输出这些点在几个区间里 (n,m<=3e5, L,R<= 1e6,所有询问 k 的和不超过 3e5 ) 

    分析

    正着做,无论是对于每个点考虑在那些区间 和 那些区间包括那些点都没有想到合适复杂度,换个角度,考虑点的补集所组成的线段集合完全包含那些线段,那么这些线段对答案一定没有贡献,

    故那么我们怎么check每个询问点的补集所组成的线段和所给线段的关系?考虑对所有询问离线,按照线段左端点为first key(从大到小), 右端点为second key(从小到大),每个线段的询问时间为third

    key(从小到大,因为当已经线段和询问线段重合的情况),这样可以保证每次询问的左端点是递减的,考虑用树状数组加速这个过程,每次更新右端点即可,依次更新即可

    #include<bits/stdc++.h>
    #define ll long long
    using namespace std;
    const int maxn = 1e6+7;
    
    
    int c[maxn];
    int ans[maxn];
    
    struct node{
        int l,r,id;
        friend bool operator < (node a,node b)
        {
            if(a.l != b.l)
                return a.l>b.l;
            else if(a.r!=b.r)
                return a.r < b.r;
            else
                return a.id<b.id;
        }
    }t[maxn];
    
    int lowbit(int x){
        return x&(-x);
    }
    int sum(int  x){
        int sum=0;
        while(x>0){
            sum+=c[x];
            x-=lowbit(x);
        }
        return sum;
    }
    int modfiy(int x,int val){
        while(x<=1000000){
            c[x]+=val;
            x+=lowbit(x);
        }
    }
    int n,q;
    int main()
    {
        scanf("%d%d",&n,&q);
        for(int i=1;i<=n;i++)
        {
            scanf("%d%d", &t[i].l,&t[i].r);
            t[i].id=0;
        }
        int k;
        int tot=n;
        for(int i=1;i<=q;i++)
        {
            scanf("%d", &k);
            int st=0;
            for(int j=1;j<=k;j++)
            {
                int x;
                scanf("%d", &x);
                if(!st)
                {
                    if(x>1)
                    {
                        t[++tot].l=1;
                        t[tot].r=x-1;
                        t[tot].id=i;
                    }
                    st=x;
                }
                else
                {
                    if(x-1<st+1)
                       {
    
                       }
                    else
                    {
                        t[++tot].l=st+1;
                        t[tot].r=x-1;
                        t[tot].id=i;
                    }
                    st=x;
                }
    
            }
           if(st+1<=1000000)
           {
              t[++tot].l=st+1;
              t[tot].r=1000000;
              t[tot].id=i;
           }
        }
        sort(t+1, t+tot+1);
        for(int i=1;i<=tot;i++)
        {
            if(t[i].id==0)
            {
                modfiy(t[i].r,1);
            }
            else
            {
                ans[t[i].id]+=sum(t[i].r);
            }
        }
        for(int i=1;i<=q;i++)
            printf("%d
    ", n-ans[i]);
        return 0;
    }

    Codeforces 375D   树上莫队

    题意

    给一颗n个节点带权树(权值=颜色),现有m个询问,每个询问问u,k,问以u为根的子树,出现的所有颜色中次数>=k次的数量(根为1,n,m<=1e5 )

    分析

    Study Linkdsu on tree

    解法:莫队/dsu on tree+bit

    莫队:求出dfs序,可知每个点的子树的范围,转化为区间上的序列的莫队,每次询问一个根节点相当于询问它的子树区间,对区间分块排序后直接上莫队即可 ( 类比区间

              trick:处理>=k的数量很巧妙啊,因为每个颜色数量是逐渐变大的,故直接用一个数组递推过去即可,O(1)

    #include<bits/stdc++.h>
    #define ll long long
    using namespace std;
    const int maxn = 1e5+7;
    
    int n,m,col[maxn],rcol[maxn],sum[maxn],cnt[maxn];
    int head[maxn*2],nxt[maxn*2],to[maxn*2],tot;
    int L[maxn],R[maxn], Ans[maxn], block, belong[maxn];
    
    void addedge(int u,int v)
    {
        to[++tot]=v;
        nxt[tot]=head[u];
        head[u]=tot;
    }
    
    struct node{
        int l,r,id,k;
        friend bool operator <(node a,node b)
        {
            if(a.l/block!=b.l/block)
                return a.l/block < b.l/block;
            return a.r<b.r;
        }
    }q[maxn];
    
    int tms;
    void dfs(int u,int fa){
        L[u]=++tms;
        for(int i=head[u];i;i=nxt[i]){
            int v=to[i];
            if(v!=fa)
                dfs(v,u);
        }
        R[u]=tms;
    }
    
    int main()
    {
        scanf("%d%d", &n, &m);
        for(int i=1;i<=n;i++)
           scanf("%d", &col[i]);
        block=int(sqrt(n));
        int u,v;
        for(int i=1;i<=n-1;i++){
            scanf("%d%d", &u, &v);
            addedge(u,v);
            addedge(v,u);
        }
        dfs(1,0);
    
        int x,kk;
        for(int i=1;i<=m;i++){
            scanf("%d%d", &x, &kk);
            q[i].l=L[x];
            q[i].r=R[x];
            q[i].k=kk;
            q[i].id=i;
        }
        sort(q+1,q+m+1);
        for(int i=1;i<=n;i++)
            rcol[i]=col[i];
        for(int i=1;i<=n;i++)
            col[L[i]]=rcol[i];
        int L=1,R=0;
        for(int i=1;i<=m;i++)
        {
            while(L<q[i].l)
            {
                sum[cnt[col[L]]]--;
                cnt[col[L]]--;
                L++;
            }
            while(L>q[i].l)
            {
                L--;
                cnt[col[L]]++;
                sum[cnt[col[L]]]++;
            }
            while(R<q[i].r)
            {
                R++;
                cnt[col[R]]++;
                sum[cnt[col[R]]]++;
            }
            while(R>q[i].r)
            {
                sum[cnt[col[R]]]--;
                cnt[col[R]]--;
                R--;
            }
            Ans[q[i].id]=sum[q[i].k];
        }
        for(int i=1;i<=m;i++)
            printf("%d
    ", Ans[i]);
        return 0;
    } 

    WC2013 糖果公园(UOJ 58)

    分析

    留坑,树上带修改莫队 ,vfk题解Link


    树上莫队  SPOJ  COT2 - Count on a tree II / BZOJ 3757

    题意

    SPOJ:给一颗点权数,多次询问路径(a,b)上有多少个权值不同的点(n<=4e5,m<=1e6)

    BZOJ:

    分析

    直观的做法:每次从lca(a,b)分别走到a,b即可,但首先从lca(a,b)出发不能保证走的一定是a,b的路径,最坏的情况是走整棵树,gg

    莫队:考虑深搜将树分块,将树上的序列转化成区间上的序列,按照区间上的莫队方法来做

    转移:考虑从(u,v)->(u',v')

    设S(u,v):u到v路径上的点集,则S(u,v)=S(root,u) xor S(root,v) xor lca(u,v) ( xor运算=对称差:简单的说就是去掉出现两次的 )

    由于lca(u,v)很麻烦,那么我们设T(u,v)=S(root,u) xor S(root,v),则T(u',v)=S(root,u') xor S(root,v) (现考虑一个点移动)

    T(u',v) xor T(u,v) = S(root,u') xor S(root,v)xor S(root,u) xor S(root,v)

    T(u',v) xor T(u,v) = S(root,u') xor S(root,u) = T(u',u)

    两边同时 xor T(u,v) 

    T(u',v) = T(u,v) xor T(u',u)

    => S(a',b) = S(a,b) xor S(a,a') xor lca(a',b)

    => S(a',b')=S(a,b) xor S(a,a') xor S(b,b') xor lca(a',b')

    所以:从(a,b)->(a',b') 只需要 a-> a' 和 b-> b'路径上的点取反 ( 除lca(u',v) )的情况取反即可 

    转移的方法:通过预处理的深度,不断调节深度,直至到达两点lca,但没有处理lca

    trick:注意lca(u',v')不是取反,直接取相反的情况即可 ( 即没有对lca(u',v')的数量修改),因为在转移的时候并没有将lca(u',v')考虑进去,

               那么有一个问题,若lca(u,v)==lca(u',v'),这样处理没问题,但lca(u,v) != lca(u',v'),其实通过画图观察不难发现,u->u'和v->v' 转移过程都没有改变lca(u,v),故不影响

               所以我们只要大胆的把u->u'和v->v' 转移过去就好,最后将lca(u',v')的贡献加进去即可

    总结:1. 深搜 分块+预处理深度           2. 求lca主       3. sort     4.莫队转移得到answer

    !!!!Trick:树上莫队按块排序 L, R的所在的块都要排序,因为在序列上(eg:数组)从小到大直接就是从近到远,但树上的序号却没有任何关系,因此必须要把L,R的块都排序

    #include<cstdio>
    #include<iostream>
    #include<algorithm>
    #include<cmath>
    #include<cstring>
    #include<unordered_map>
    using namespace std;
    
    const int maxn = 4e5+7;
    
    int head[maxn],nxt[maxn*2],to[maxn*2],tot;
    int col[maxn],n,m,u,v,ans[maxn],Ans;
    int belong[maxn],block;
    int fa[maxn][32],d[maxn],f[maxn];
    bool vis[maxn],vv[maxn];
    int g[maxn];
    unordered_map<int,int>rg;
    
    struct node{
        int l,r,id;
        friend bool operator < (node a,node b){
            if(belong[a.l] != belong[b.l])  return belong[a.l] < belong[b.l];
            else return belong[a.r] < belong[b.r];
        }
    }q[maxn];
    
    
    void addedge(int u,int v)
    {
        to[++tot]=v;
        nxt[tot]=head[u];
        head[u]=tot;
    }
    
    int s[maxn],cnt,sz[maxn],num;
    void dfs(int x)
    {
        vis[x]=1;
        s[++cnt]=x;
        for(int i=head[x];i;i=nxt[i])
        {
            int v=to[i];
            if(!vis[v])
            {
                d[v]=d[x]+1;
                fa[v][0]=x;
                dfs(v);
                if(sz[x] + sz[v] >= block)
                {
                    ++num;
                    sz[x]=0;
                    while(s[cnt]!=x)
                    {
                        belong[s[cnt]]=num;
                        vv[s[cnt]]=1;
                        --cnt;
                    }
                }
                else
                    sz[x]+=sz[v];
            }
        }
        sz[x]++;
    }
    
    void init_rmq()
    {
       // fa[1][0]=1;
        for(int j=1;j<=30;j++)
            for(int i=1;i<=n;i++)
            fa[i][j]=fa[fa[i][j-1]][j-1];
    }
    
    int LCA(int u,int v)
    {
        if(d[u]<d[v])
            swap(u,v);
        int dc=d[u]-d[v];
        for(int i=0;i<30;i++){ //�����度
            if((1<<i)&dc)
                u=fa[u][i];
        }
        if(u==v) return u;      // �个��好���个��lca
        for(int i=30;i>=0;i--){ //���以跳��大�步��使�(u,v)�lca��
            if(fa[u][i] != fa[v][i])
            {
                u=fa[u][i];
                v=fa[v][i];
            }
        }
        return  fa[u][0]; //u�v�����lca���
    }
    
    void rev(int x)
    {
        if(!vis[x]){
            f[col[x]]++;
            if(f[col[x]]==1)
                Ans++;
            vis[x]=1;
        }
        else{
            f[col[x]]--;
            if(f[col[x]]==0)
                Ans--;
            vis[x]=0;
        }
    }
    
    void solve(int u,int v)
    {
        while(u!=v)
        {
            if(d[u]<d[v]){
                rev(v),v=fa[v][0];
            }
            else
            {
                rev(u),u=fa[u][0];
            }
        }
    }
    
    int main()
    {
        scanf("%d%d", &n, &m);
        block=int(sqrt(n));
        for(int i=1;i<=n;i++)
        {
            scanf("%d", &col[i]);
            g[i]=col[i];
        }
        sort(g+1,g+n+1);
        int point=unique(g+1,g+n+1)-g-1;
        for(int i=1;i<=point;i++)
            rg[g[i]]=i;
        for(int i=1;i<=n;i++)
          col[i]=rg[col[i]];
        for(int i=1;i<=n-1;i++)
        {
            scanf("%d%d", &u, &v);
            addedge(u,v),addedge(v,u);
        }
      //  d[1]=1;
        dfs(1);
        ++num;
        for(int i=1;i<=n;i++){
            if(!vv[i])
                belong[i]=num;
        }
        init_rmq();
        memset(vis,0,sizeof(vis));
        for(int i=1;i<=m;i++){
            scanf("%d%d", &u, &v);
            if(belong[u] > belong[v])
                swap(u,v);
            q[i].l=u;
            q[i].r=v;
            q[i].id=i;
        }
        sort(q+1,q+m+1);
        int lca=LCA(q[1].l,q[1].r);
        solve(q[1].l,q[1].r);
        ans[q[1].id]=Ans+!(f[col[lca]]);
        for(int i=2;i<=m;i++)
        {
            lca=LCA(q[i].l,q[i].r);
            solve(q[i-1].l,q[i].l);
            solve(q[i-1].r,q[i].r);
            ans[q[i].id]=Ans+!(f[col[lca]]);
        }
        for(int i=1;i<=m;i++)
            printf("%d
    ",ans[i]);
        return 0;
    }
  • 相关阅读:
    多线程学习纪要
    字符编码ASCII、Unicode、UTF-8以及验证
    C# 自定义特性Attribute要点
    Java --- 流操作
    数据库 --- 索引
    Java -- 枚举
    mybatis --- 使用通用mapper或者mybatis-plus进行映射时,当数据库中有大写字母,自定义映射的字段名。
    后端 --- 快速创建一个sb项目 (使用spring官网的https://start.spring.io)
    数据库 --- decimal类型测试
    Java --- 线上故障调优01 打印堆栈信息
  • 原文地址:https://www.cnblogs.com/Deadline/p/9163421.html
Copyright © 2011-2022 走看看