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;
    }
    
}
posted @ 2021-08-07 22:44  darker_wxl  阅读(285)  评论(2编辑  收藏  举报
window.onload = function(){ $("#live2dcanvas").attr("style","position: fixed; opacity: 0.7; left: 70px; bottom: 0px; z-index: 1; pointer-events: none;") }