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]," \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;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章