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]," \n"[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\n",(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\n",ans);
}

int main(){
//    freopen("C.in","r",stdin);
    int T;scanf("%d",&T);
    while(T--) Solve();
    return 0;
}

D. 蚂蚁

先考虑不需要支持修改怎么做:假设现在有 $k$ 只蚂蚁,所在的点分别为 $v_1\ldots 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\n",query(1,1,n,1,t)-1);
    }
    return 0;
}

E. 划分

为啥完全没有思路。。。

我们考虑固定 $1$ 为根,定义每个连通块的根为连通块深度最小的点,我们枚举其中一个连通块的根为 $x \neq 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\n",ans);
    return 0;
}
Last modification:October 31st, 2020 at 03:42 pm
如果觉得我的文章对你有用,请随意赞赏