2021牛客暑期多校訓練營7


2021牛客暑期多校訓練營7

F. xay loves trees

  • 題意

給你兩棵樹,每棵樹都給出了n個點n-1條邊,你需要找到一個最大的點集合\(\left \{ 1,2,\cdots ,n \right \}\),滿足

  1. 在第一棵樹中滿足集合中的任意兩點之間都滿足祖先和子節點的關系。
  2. 在第二棵樹中,則一定不存在上述關系。
  • 思路

本題做法比較多,但思路都是先跑一邊第二棵樹的dfs序
那么為什么要跑dfs序呢?

  • dfs序中,若某個點為根節點的話,那么它一定比它的所有子樹先跑dfs,那么所有的子樹dfs序中的編號都大於它,那么我們可以記錄每棵樹作為根節點,它的子節點個數為\(sz_i\)
  • 然后,以它這棵樹編號i為起點,\(i + sz_i\) 就是它的子節點中編號最大的,而且\(i \to i + sz_i\)這段都是連續的,且都是i的子樹。
  • 然后,我們可以用線段樹或者一些stl,通過維護dfs序來維護以i為根節點,所有子樹的信息。

跑完之后,我們考慮本題該怎么處理祖先和子節點的關系。
可以想到,我們肯定是現在通過用dfs跑第一棵樹,跑每一條鏈(一棵樹中,每一條樹鏈都滿足,祖先和子節點都是有關聯的),然后更新第二棵樹的dfs序中的信息判斷是否滿足答案即可。
那么本題可以通過線段樹維護第二棵樹上每個點的max信息,通過遍歷第一棵樹的每一條鏈,遍歷到某個點時,把該點在第二棵樹上所有子樹的sum也加1,那么,當根節點的sum為1的時候表示第二棵樹中不存在某條鏈中有兩個點。

code :

int n;
vi g1[N],g2[N];

int in[N],out[N], cnt;
int sum[N << 2],laz[N << 2];
#define mid (l + r >> 1)
#define lsn (u << 1)
#define rsn (u << 1 | 1)
void dfs1(int u,int fa) {
    in[u] = ++ cnt;
    for(auto it : g2[u]) {
        if(it == fa) continue;
        dfs1(it,u);
    }
    out[u] = cnt;
}
void up(int u) {
    sum[u] = max(sum[lsn],sum[rsn]);
}

void down(int u,int l,int r) {
    if(laz[u]) {
        sum[lsn] += laz[u];
        sum[rsn] += laz[u];
        laz[lsn] += laz[u];
        laz[rsn] += laz[u];
        laz[u] = 0;
    }
}
void build(int u = 1,int l = 1,int r = n) {
    sum[u] = laz[u] = 0;
    if(l == r) {
        return;
    }
    build(lsn,l,mid);
    build(rsn,mid + 1,r);
}

void update(int L,int R,int v,int u = 1,int l = 1,int r = n) {
    if(L > r || l > R) return;
    if(L <= l && R >= r) {
        sum[u] += v;
        laz[u] += v;
        return;
    }
    down(u,l,r);
    if(L <= mid) update(L,R,v,lsn,l,mid);
    if(R > mid) update(L,R,v,rsn,mid + 1,r);
    up(u);
}

int stk[N];
int hh,tt;
int ans;

void dfs2(int u,int fa) {
    int now = -1;
    stk[++ tt] = u;
    // 更新
    update(in[u],out[u],1);
    if(sum[1] == 1) {
        ans = max(ans, tt - hh + 1);
    }else if(tt - hh + 1 > ans){
        now = stk[hh ++];
        update(in[now],out[now],-1);
    }
    for(auto it : g1[u]) {
        if(it == fa) continue;
        dfs2(it,u);
    }
    // 回溯
    if(now != -1) {
        update(in[now],out[now],1);
        stk[-- hh] = now;
    }
    update(in[u],out[u],-1);
    tt --; // 重要,這樣后面的父節點中的兒子之間就一定無關聯
}

void init(int k) {
    cnt = 0;
    for(int i = 1;i <= k;i ++) g1[i].clear(),g2[i].clear();
}

void solve(){
    
    cin >> n;
    build();
    for(int i = 1;i < n;i ++) {
        int a,b;
        cin >> a >> b;
        g1[a].pb(b);
        g1[b].pb(a);
    }
    for(int i = 1;i < n;i ++) {
        int a,b;
        cin >> a >> b;
        g2[a].pb(b);
        g2[b].pb(a);
    }
    dfs1(1,0); // 找到dfs序
    hh = 0,tt = -1;
    ans = 0;
    dfs2(1,0);
    cout << ans << endl;
    init(n);
}

H. loves count

  • 題意

給出一組序列,問存在多少對三元組(i,j,k)滿足\(a_i \times a_i = a_k\).

  • 思路

emmmm,好像因為啥調和級數,反正暴力就對了

code :(略)

I. xay loves or

  • 題意

給你x和s,為你存在多少個y滿足\(x \ or \ y = s\)

  • 思路

二進制判斷即可,別忘了y = 0的情況即可

code :

void solve(){
    ll n,s;
    cin >> n >> s;
    int cnt = 0;
    int c = 0;
    for(int i = 31;i>=0;i --) {
        int k = s >> i & 1;
        int b = n >> i & 1;
        if(b && !k) {
            cout << "0" << endl;
            return;
        }
        if(k) cnt ++;
        if(b) c ++;
    }
    if(n == s){
        cout << pow_mod(2,c) - 1 << endl;
    }else {
        cout << pow_mod(2,c) << endl;
    }
    
}


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM