【Henu ACM Round#15 E】 A and B and Lecture Rooms

【链接】 我是链接,点我呀:)
【题意】

在这里输入题意

【题解】

最近公共祖先。
(树上倍增

一开始统计出每个子树的节点个数_size[i]

如果x和y相同。
那么直接输出n.

否则求出x和y的最近公共祖先。z
(假定y的深度大于x

【1】如果z等于x或y中的一个。
那么久就找到x..y的路径(长度设为L)中的中点u。

显然,u和它的其他len-1个子树上的任意一个节点都是可行的(除了那个包含y的子树
设_get(x,step)表示x节点往上走step步到达的节点
则输出_sum[中点]-_sum[ _get(y,L/2) ]即可

【2】如果z不等于x和y中的任意一个。
①x和y往上走的距离是一样的。
->那么z的子树中,除了这两个节点往上走上来的子树,
其他子树里面的节点都是可行的
②x和y往上走的距离不一样。
->那么就还是和【1】中的情况一样,找出中点即可。

_get(x,step)函数可以用树上倍增的p数组实现。

【代码】

#include <bits/stdc++.h>
using namespace std;

const int MAXN = 1e5+10;
const int MAX = 17;

vector <int> son[MAXN],g[MAXN];
int n,p[MAXN][MAX+5],dep[MAXN],pre[MAX+5],m;
int _size[MAXN];

void dfs(int x,int f)
{
    _size[x] = 1;
    dep[x] = dep[f] + 1;
    p[x][0] = f;
    for (int i = 1; i <= MAX; i++) p[x][i] = p[p[x][i - 1]][i - 1];
    int len = g[x].size();
    for (int i = 0; i <= len - 1; i++)
    {
        int y = g[x][i];
        if (y != f) {
            son[x].push_back(y);
            dfs(y, x);
            _size[x]+=_size[y];
        }
    }
}

int _get(int x,int ma){
    for (int i = MAX;i>=0;i--){
        if (ma>=pre[i]){
            ma-=pre[i];
            x = p[x][i];
        }
    }
    return x;
}

int main()
{
    ios::sync_with_stdio(0),cin.tie(0);
    #ifdef LOCAL_DEFINE
        freopen("rush.txt","r",stdin);
    #endif
    pre[0] = 1;
    for (int i = 1; i <= MAX; i++)
        pre[i] = pre[i - 1] << 1;
    cin >> n;
    for (int i = 1; i <= n; i++)
        son[i].clear();
    for (int i = 1; i <= n - 1; i++)
    {
        int x, y;
        cin >> x >> y;
        g[x].push_back(y);
        g[y].push_back(x);
    }
    dfs(1, 0);
    cin >> m;
    for (int i = 1; i <= m; i++)
    {
        int t0, t1,pret1,pret0;
        cin >> t0 >> t1;
        if(t0==t1){
            cout<<n<<endl;
            continue;
        }
        if (dep[t0] > dep[t1]) swap(t0, t1);
        pret1 = t1;
        pret0 = t0;
        int dist0 = 0,dist1 = 0;
        for (int i = MAX; i >= 0; i--)
            if (dep[t0] <= dep[t1] - pre[i]){
                t1 = p[t1][i];
                dist1 += pre[i];
            }
        //t1??t0?????????
        if (t1 == t0)
        {
            if ((dist0+dist1)%2==0){
                int dis = (dist0+dist1)/2;
                int special = _get(pret1,dis);
                cout << _size[special] - _size[_get(pret1,dis-1)]<<endl;
            }else{
                cout<<0<<endl;
            }
            continue;
        }
        for (int i = MAX; i >= 0; i--)
        {
            if (p[t0][i] == p[t1][i]) continue;
            dist0+=pre[i];dist1+=pre[i];
            t0 = p[t0][i], t1 = p[t1][i];
        }
        dist0+=pre[0],dist1+=pre[0];
        t0 = p[t0][0];
        if (dist0==dist1){
            cout << _size[1]-_size[_get(pret0,dist0-1)]-_size[_get(pret1,dist1-1)] << endl;
        }else{
            if ((dist0+dist1)%2==0){
                int dis = (dist0+dist1)/2;
                int special = _get(pret1,dis);
                cout << _size[special] - _size[_get(pret1,dis-1)]<<endl;
            }else{
                cout<<0<<endl;
            }
        }
    }

    return 0;
}

原文地址:https://www.cnblogs.com/AWCXV/p/8350586.html

时间: 2024-08-27 04:40:38

【Henu ACM Round#15 E】 A and B and Lecture Rooms的相关文章

【Henu ACM Round#15 A】 A and B and Chess

[链接] 我是链接,点我呀:) [题意] 在这里输入题意 [题解] 统计大写和小写的个数. 比较答案.输出即可. [代码] #include <bits/stdc++.h> using namespace std; string s[10]; map<char,int> dic; int inc[300]; int main() { for (int i = 0;i < 8;i++) cin >> s[i]; for (int i = 0;i < 8;i++

【Henu ACM Round#15 C】 A and B and Team Training

[链接] 我是链接,点我呀:) [题意] 在这里输入题意 [题解] 枚举第一种方法. 剩下的全都个第二种方法. 看看能组成多少个队伍就可以了. [代码] #include <bits/stdc++.h> #define ll long long using namespace std; int n,m; int main() { ios::sync_with_stdio(0),cin.tie(0); cin >> n >> m; int ans = 0; for(int

【Henu ACM Round#15 F】Arthur and Questions

[链接] 我是链接,点我呀:) [题意] 在这里输入题意 [题解] a1+a2+...+aka1<ak+1 a2+a3+...+ak+1a2<ak+2 类似还可以推出 a3<ak+3 a4<ak+4 ... 则有 a1<ak+1<a2k+1<a3k+1... a2<ak+2<a2k+2<a3k+2... ... 也就是每隔k个要是递增的. 你的任务就是维护每隔k个数字递增. 我们先考虑a[1],a[1+k],a[1+2k],a[1+3k]...这

【Henu ACM Round #13 C】 Ebony and Ivory

[链接] 我是链接,点我呀:) [题意] 在这里输入题意 [题解] 先求出c-bx的所有可能 ->存在map里面 然后枚举y看看ay在不在map里面 在的话就有解. 这样复杂度是\(O(N*log_2N)\)的 比直接两层循环枚举的\(O(N^2)\)复杂度要来的好 这种方法也叫"中途相遇法" [代码] #include <bits/stdc++.h> #define ll long long using namespace std; ll a,b,c; map<

【Henu ACM Round #13 B】Spider Man

[链接] 我是链接,点我呀:) [题意] 在这里输入题意 [题解] 会发现每个环可以操作的次数是固定的. (环的大小-1 也就是说一旦环确定了.其实结果就已经确定了. 则直接看操作总数的奇偶性就可以了. [代码] #include <bits/stdc++.h> #define long long using namespace std; int n,x; int main(){ #ifdef LOCAL_DEFINE freopen("rush_in.txt", &quo

【Henu ACM Round #13 D】A Trivial Problem

[链接] 我是链接,点我呀:) [题意] 在这里输入题意 [题解] 123...n中末尾0的个数 只会由素因子2和5的个数决定且等于 Min{cnt[2],cnt[5]} 且素因子2的个数一定会比5多; 所以n!的末尾0的个数 等于for (int i = 1;i <= n;i++)中所有i的5因子的个数和 枚举一下就好 [代码] #include <bits/stdc++.h> using namespace std; const int N = 1e7; int n; vector&

【Henu ACM Round #13 E】Spy Syndrome 2

[链接] 我是链接,点我呀:) [题意] 在这里输入题意 [题解] 对m个串建立一棵字典树. 然后对主串. 尝试从第一个位置开始.在字典树中尝试匹配 如果匹配到了位置i 就再从位置i+1开始尝试匹配 (这时又重新从根节点开始重新匹配 每次匹配最多只要往下走50步. 写个递归的过程就好. [代码] #include <bits/stdc++.h> using namespace std; const int N = 1e6; const int NN = 1e4; const int M = 1

【Henu ACM Round#14 A】Vitaly and Night

[链接] 我是链接,点我呀:) [题意] 在这里输入题意 [题解] 连续两个如果不全是0就递增cnt [代码] #include <bits/stdc++.h> using namespace std; int n,m; int main() { cin >> n >>m; int cnt = 0; for (int i = 1;i <= n;i++){ int x,y; for (int j = 1;j <= m;j++){ cin >>x &

【Henu ACM Round#14 C】Duff and Weight Lifting

[链接] 我是链接,点我呀:) [题意] 在这里输入题意 [题解] 2^y可以由两个2^(y-1)相加得到. 则有一个贪心的策略. 就是2^x尽量都变成2^(x+1) (即能够凑就尽量凑) 如果x还有剩余的话.答案递增1 而凑上去的数字,显然是可以合并成1步操作的.因为他们的和就是2^(x+1) [代码] #include <bits/stdc++.h> #define ll long long using namespace std; const int N = 1e6; int n,a[N