• ZR 2020普转提七连测day5


    A. 跳石头

    首先我们观察一下青蛙是怎么跳的:对于一个断点 ((i,i+1)),如果有 (x) 只青蛙往左跳,那么一定有 (x) 只青蛙往右跳,所以 (a_i) 一定是偶数。

    并且我们发现从 (a_i)(a_{i+1}),变化的只可能是 (i+1) 这一个青蛙,也就是有 (|a_i-a_{i+1}| in {0,2,-2})

    我们考虑 (a_i)(a_{i+1}) 的变化:如果是 (0) 说明一定是 (i) 跳到自己,如果多了 (2) 说明 (i) 往后跳,少了 (2) 是往前跳。

    所以我们可以得出每个点往哪里跳,然后用类似括号序列的方式配对就行了:遇到往右的加入栈,往左的就随便取出一个。

    #include <bits/stdc++.h>
    
    #define fi first
    #define se second
    #define db double
    #define U unsigned
    #define P std::pair<int,int>
    #define LL long long
    #define pb push_back
    #define MP std::make_pair
    #define all(x) x.begin(),x.end()
    #define CLR(i,a) memset(i,a,sizeof(i))
    #define FOR(i,a,b) for(int i = a;i <= b;++i)
    #define ROF(i,a,b) for(int i = a;i >= b;--i)
    #define DEBUG(x) std::cerr << #x << '=' << x << std::endl
    
    const int MAXN = 2e5 + 5;
    int n,a[MAXN];
    int ans[MAXN];
    
    int main(){
        scanf("%d",&n);
        FOR(i,1,n-1) scanf("%d",a+i);
        bool flag = 1;
        FOR(i,1,n){
            flag &= !(a[i]&1);
            flag &= (std::abs(a[i]-a[i-1]) == 2 || a[i] == a[i-1]);
        }
        if(!flag){
            puts("No");
            return 0;
        }
        std::vector<int> S;
        FOR(i,1,n){
            if(a[i] == a[i-1]+2){// 左括号
                S.pb(i);
            }
            else if(a[i] == a[i-1]){
                ans[i] = i;
            }
            else{ // 右括号
                if(S.empty()){
                    puts("No");
                    return 0;
                }
                ans[S.back()] = i;
                ans[i] = S.back();
                S.pop_back();
            }
        }
        if(!S.empty()){
            puts("No");
            return 0;
        }
        puts("Yes");
        FOR(i,1,n) printf("%d%c",ans[i]," 
    "[i==n]);
        return 0;
    }
    /*
    1. a[i] 都是偶数 并且正好一半往左一半往右
    2.  考虑 a[i] 和 a[i+1]:如果中间不动 a[i]==a[i+1],如果中间往右 a[i+1] = a[i]+2,如果中间往左 a[i+1] = a[i]-2
    往右必然要有一个往左 所以遇到往右的可以看成左括号 往左的可以看成右括号 就行了
    */
    

    B. 树

    先把颜色相同的缩成一个树,感性理解操作肯定是每次操作同一个联通块,让他的影响不断增大。我们设第一次操作的点是 (x) ,以 (x) 为根建树,发现操作最大深度次就能完成。所以我们要让最大深度尽量小,就是直径的长度除以二。

    稍微注意下处理重边即可。

    #include <bits/stdc++.h>
    
    #define fi first
    #define se second
    #define db double
    #define U unsigned
    #define P std::pair<int,int>
    #define LL long long
    #define pb push_back
    #define MP std::make_pair
    #define all(x) x.begin(),x.end()
    #define CLR(i,a) memset(i,a,sizeof(i))
    #define FOR(i,a,b) for(int i = a;i <= b;++i)
    #define ROF(i,a,b) for(int i = a;i >= b;--i)
    #define DEBUG(x) std::cerr << #x << '=' << x << std::endl
    
    const int MAXN = 5e5 + 5;
    int n,a[MAXN];
    std::vector<int> G[MAXN],T[MAXN];
    bool vis[MAXN];
    int f[MAXN];
    
    inline int find(int x){
        return x == f[x] ? x : f[x] = find(f[x]);
    }
    
    inline void merge(int x,int y){
        x = find(x);y = find(y);
        if(x == y) return;
        f[x] = y;
    }
    
    inline void dfs(int v){
        vis[v] = 1;
        for(auto x:G[v]){
            if(vis[x]) continue;
            if(a[x] != a[v]) continue;
            merge(v,x);
            dfs(x);
        }
    }
    
    int g[MAXN][2],ans;
    
    inline void dfs2(int v,int fa=0){
        for(auto x:T[v]){
            if(x == fa) continue;
            dfs2(x,v);
            if(g[v][0] < g[x][0]+1){
                g[v][1] = g[v][0];
                g[v][0] = g[x][0]+1;
            }
            else if(g[v][1] < g[x][0]+1){
                g[v][1] = g[x][0]+1;
            }
        }
        ans = std::max(ans,g[v][0]+g[v][1]);
    }
    
    std::map<P,int> S;
    
    int main(){
        scanf("%d",&n);FOR(i,1,n) scanf("%d",a+i),f[i] = i;
        FOR(i,2,n){
            int u,v;scanf("%d%d",&u,&v);G[u].pb(v);G[v].pb(u);
        }
        FOR(i,1,n){
            if(vis[i]) continue;
            dfs(i);
        }
        FOR(v,1,n){
            for(auto x:G[v]){
                if(find(x) != find(v)){
                    int a = find(x),b = find(v);
                    if(a > b) std::swap(a,b);
                    S[MP(a,b)] = 1;
                }
            }
        }
        for(auto x:S){
            T[x.fi.fi].pb(x.fi.se);
            T[x.fi.se].pb(x.fi.fi);
        }
        dfs2(find(1));
        printf("%d
    ",(ans+1)/2);
        return 0;
    }
    

    C. 背包

    如果没有背包的限制,那么答案就是按照重量从大到小排序,求价值的 LDS。

    我们按照重量从大到小排序,那么位置 (i) 我们能得出最多能选几个物品,设 (f_i) 表示考虑了前 (i) 个的答案,树状数组优化转移后特判一下能选几个物品的数量限制就行了。也就是 (f_i = min(f_i,lim_i))

    #include <bits/stdc++.h>
    
    #define fi first
    #define se second
    #define db double
    #define U unsigned
    #define P std::pair<int,int>
    #define LL long long
    #define pb push_back
    #define MP std::make_pair
    #define all(x) x.begin(),x.end()
    #define CLR(i,a) memset(i,a,sizeof(i))
    #define FOR(i,a,b) for(int i = a;i <= b;++i)
    #define ROF(i,a,b) for(int i = a;i >= b;--i)
    #define DEBUG(x) std::cerr << #x << '=' << x << std::endl
    
    const int MAXN = 2e5 + 5;
    
    struct BIT{
        #define lowbit(x) ((x)&(-(x)))
        int tree[MAXN];
    
        inline void add(int pos,int x){
            while(pos){
                tree[pos] = std::max(tree[pos],x);
                pos -= lowbit(pos);
            }
        }
    
        inline int query(int pos){
            int res = 0;
            while(pos < MAXN){
                res = std::max(res,tree[pos]);
                pos += lowbit(pos);
            }
            return res;
        }
    }bit;
    
    int n;
    P a[MAXN];
    int lim[MAXN],w[MAXN],m;
    
    inline void Solve(){
        scanf("%d",&n);std::vector<int> S;CLR(bit.tree,0);
        FOR(i,1,n) scanf("%d%d",&a[i].fi,&a[i].se),S.pb(a[i].se);
        std::sort(all(S));S.erase(std::unique(all(S)),S.end());
        FOR(i,1,n) a[i].se = std::lower_bound(all(S),a[i].se)-S.begin()+1;
        std::sort(a+1,a+n+1,[&](const P &x,const P &y){return x.fi > y.fi || (x.fi == y.fi && x.se > y.se);});
        scanf("%d",&m);
        FOR(i,1,m) scanf("%d",w+i);std::sort(w+1,w+m+1);std::reverse(w+1,w+m+1);
        int ans = 0;
        FOR(i,1,n){
            lim[i] = lim[i-1];
            while(lim[i]+1 <= m && w[lim[i]+1] >= a[i].fi) ++lim[i];
            int f = bit.query(a[i].se)+1;f = std::min(f,lim[i]);
            bit.add(a[i].se,f);
            ans = std::max(ans,f);
        }
        printf("%d
    ",ans);
    }
    
    int main(){
    //    freopen("C.in","r",stdin);
        int T;scanf("%d",&T);
        while(T--) Solve();
        return 0;
    }
    

    D. 蚂蚁

    先考虑不需要支持修改怎么做:假设现在有 (k) 只蚂蚁,所在的点分别为 (v_1ldots v_k),设每个蚂蚁到达根的时间为 (t_i),答案就是 (max t_i),我们观察一下有哪些限制:

    首先,每个蚂蚁的 (t_i geq dep_{v_i}),因为就算从开始爬也要花这些时间才能过去。

    然后,我们要保证不会在某时刻在同一个点,我们可以发现如果两只蚂蚁在某个时候在同一个点上,那么它们在根的时候也会在同一个点上,所以我们只需要保证 (t_i) 互不相同就行了。

    所以问题变成了,每个变量有一个下界 (lim_i),要求分配使得变量互不相同,并且最大值尽量小,这是个经典问题:考虑记 (s_i = sum_{x=1}^k [lim_x geq i]),答案显然是 (max(s_i+i-1)),用线段树维护即可。

    #include <bits/stdc++.h>
    
    #define fi first
    #define se second
    #define db double
    #define U unsigned
    #define P std::pair<int,int>
    #define LL long long
    #define pb push_back
    #define MP std::make_pair
    #define all(x) x.begin(),x.end()
    #define CLR(i,a) memset(i,a,sizeof(i))
    #define FOR(i,a,b) for(int i = a;i <= b;++i)
    #define ROF(i,a,b) for(int i = a;i >= b;--i)
    #define DEBUG(x) std::cerr << #x << '=' << x << std::endl
    
    const int MAXN = 1e5 + 5;
    
    int mx[MAXN<<2],tag[MAXN<<2];
    #define lc ((x)<<1)
    #define rc ((x)<<1|1)
    
    inline void cover(int x,int d){
        mx[x] += d;tag[x] += d;
    }
    
    inline void pushdown(int x){
        if(tag[x]){
            cover(lc,tag[x]);
            cover(rc,tag[x]);
            tag[x] = 0;
        }
    }
    
    inline void modify(int x,int l,int r,int L,int R,int d){
        if(l == L && r == R){
            cover(x,d);
            return;
        }
        int mid = (l + r) >> 1;pushdown(x);
        if(R <= mid) modify(lc,l,mid,L,R,d);
        else if(L > mid) modify(rc,mid+1,r,L,R,d);
        else modify(lc,l,mid,L,mid,d),modify(rc,mid+1,r,mid+1,R,d);
        mx[x] = std::max(mx[lc],mx[rc]);
    }
    
    inline void build(int x,int l,int r){
        tag[x] = 0;
        if(l == r){
            mx[x] = l;return;
        }
        int mid = (l + r) >> 1;
        build(lc,l,mid);build(rc,mid+1,r);
        mx[x] = std::max(mx[lc],mx[rc]);
    }
    
    int n,m;
    std::vector<int> G[MAXN];
    int dep[MAXN];
    
    inline void dfs(int v,int fa=0){
        dep[v] = dep[fa]+1;
        for(auto x:G[v]) if(x != fa) dfs(x,v);
    }
    
    inline int query(int x,int l,int r,int L,int R){
        if(L > R) return 1;
        if(l == L && r == R) return mx[x];
        int mid = (l + r) >> 1;pushdown(x);
        if(R <= mid) return query(lc,l,mid,L,R);
        if(L > mid) return query(rc,mid+1,r,L,R);
        return std::max(query(lc,l,mid,L,mid),query(rc,mid+1,r,mid+1,R));
    }
    
    std::multiset<int> S;
    
    int main(){
        scanf("%d%d",&n,&m);
        FOR(i,2,n){
            int f;scanf("%d",&f);
            G[f].pb(i);
        }
        dfs(1);build(1,1,n);
        S.insert(0);
        FOR(i,1,m){
            int opt,x;scanf("%d%d",&opt,&x);
            if(opt == 1){
                if(x != 1){
                    S.insert(dep[x]);
                    modify(1,1,n,1,dep[x],1);
                }
            }
            else{
                if(x != 1){
                    S.erase(S.find(dep[x]));
                    modify(1,1,n,1,dep[x],-1);
                }
            }
            int t = *S.rbegin();
            printf("%d
    ",query(1,1,n,1,t)-1);
        }
        return 0;
    }
    

    E. 划分

    为啥完全没有思路。。。

    我们考虑固定 (1) 为根,定义每个连通块的根为连通块深度最小的点,我们枚举其中一个连通块的根为 (x eq 1),那么有一个连通块根一定为 (1),我们设剩下的那个连通块的根为 (y),分类讨论:

    如果 (sz_x = |A|)

    • 如果 (y) 不是 (x) 的祖先,(sz_y = sz_x)(sz_y=n-2sz_x)
    • 如果 (y)(x) 的祖先,(sz_y=2sz_x)(sz_y=n-sz_x)

    如果 (sz_x = |C|)

    • 如果 (y) 不是 (x) 的祖先,(sz_y=frac{n-sz_x}{2})
    • 如果 (y)(x) 的祖先,(sz_y = frac{n-sz_x}{2}+sz_x = frac{n+sz_x}{2})

    相当于我们对于一个点,想要询问到根的链上是否有点的 (sz) 是某个值,这个可以 dfs 的时候处理一个数组搞出来;还需要询问某个点子树外并且不是它的祖先的所有点中是否有点 (sz) 是某个值,预先处理所有的点的 (sz) 出现情况,补集转化后变成了求子树内某个值的出现次数,这个可以用类似差分计算增量的方法实现,也就是在进入子树前记录一下要查询的值,然后进入这个子树,遍历完所有的点并且加到桶里后再求一下值,做个差就能得到子树的值。详情可以看代码

    #include <bits/stdc++.h>
    
    #define fi first
    #define se second
    #define db double
    #define U unsigned
    #define P std::pair<int,int>
    #define LL long long
    #define pb push_back
    #define MP std::make_pair
    #define all(x) x.begin(),x.end()
    #define CLR(i,a) memset(i,a,sizeof(i))
    #define FOR(i,a,b) for(int i = a;i <= b;++i)
    #define ROF(i,a,b) for(int i = a;i >= b;--i)
    #define DEBUG(x) std::cerr << #x << '=' << x << std::endl
    
    const int MAXN = 1e6 + 5;
    
    std::vector<int> G[MAXN];
    int n;
    
    int sz[MAXN];
    int c1[MAXN],c2[MAXN],c3[MAXN];
    // c1: 整棵树
    // c2: 查询子树用
    // c3: 祖先
    
    inline void dfs1(int v,int fa=0){
        sz[v] = 1;
        for(auto x:G[v]){
            if(x == fa) continue;
            dfs1(x,v);
            sz[v] += sz[x];
        }
        ++c1[sz[v]];
    }
    
    int ans = 0;
    
    inline void dfs2(int v,int fa=0){
        if(2*sz[v] <= n && c3[2*sz[v]] && 2*sz[v] < n) ans = std::max(ans,sz[v]);
        if(c3[n-sz[v]] && 2*sz[v] < n) ans = std::max(ans,sz[v]);
        if(!((n+sz[v])&1) && c3[(n+sz[v])>>1]) ans = std::max(ans,(n-sz[v])>>1);
        int p1 = c2[sz[v]],p2 = n-2*sz[v] > 0 ? c2[n-2*sz[v]] : -1e9,p3 = (n-sz[v])&1 ? -1e9 : c2[(n-sz[v])>>1];
        ++c3[sz[v]];++c2[sz[v]];
        for(auto x:G[v]){
            if(x == fa) continue;
            dfs2(x,v);
        }
        p1 = c1[sz[v]]-(c2[sz[v]]-p1);
        if(p2 != -1e9) p2 = c1[n-2*sz[v]]-(c2[n-2*sz[v]]-p2);
        if(p3 != -1e9) p3 = c1[(n-sz[v])>>1]-(c2[(n-sz[v])>>1]-p3);
        if(p1 > 0 && 2*sz[v] < n) ans = std::max(ans,sz[v]);
        if(p2 > 0 && 2*sz[v] < n) ans = std::max(ans,sz[v]);
        if(p3 > 0) ans = std::max(ans,(n-sz[v])>>1);
        --c3[sz[v]];
    }
    
    int main(){
        scanf("%d",&n);
        FOR(i,2,n){
            int u,v;scanf("%d%d",&u,&v);
            G[u].pb(v);G[v].pb(u);
        }
        dfs1(1);dfs2(1);
        printf("%d
    ",ans);
        return 0;
    }
    
  • 相关阅读:
    Java中关键字优先级
    HashMap
    Mabatis通用SQL语句
    下滑线转换为驼峰
    常见问题整理
    常见问题整理
    VUE学习笔记(二)
    VUE学习笔记(一)
    获取鼠标位置
    VirtualBox NAT模式,设置虚拟机可上网,宿主机可访问虚拟机的方法
  • 原文地址:https://www.cnblogs.com/rainair/p/14304975.html
Copyright © 2020-2023  润新知