DFS序+BIT离线统计子树中小于该节点的数量+三元计数

#include <bits/stdc++.h>

using namespace std;
#define ll long long
#define mem(a, b) memset(a,b,sizeof(a))
#define INF 0x3f3f3f3f
#define p_queue priority_queue
#define DBG printf("this is a input\n")
#define fi first
#define se second
#define mk(a, b) make_pair(a,b)
#define pb(a) push_back(a)

int T;
int n ;
int p[5];
int head[200005], cnt;
struct e{
    int t, next;
}edge[400005];
vector<pair<int,int> > son[100005];
struct BIT
{
    int tree[200005] ; //开 1 倍空间
    void init()
    {
        mem(tree,0) ;
    }
    int lowbit(int k)
    {
        return k & -k;
    }
    void add(int x , int k)  // a[x] += k
    {
        while(x <= n)  //维护的是 [1 , n] 的序列
        {
            tree[x] += k ;
            x += lowbit(x) ;
        }
    }
    int sum(int x)  // sum[l , r] = sum(r) - sum(l - 1)
    {
        int ans = 0 ;
        while(x != 0)
        {
            ans += tree[x] ;
            x -= lowbit(x) ;
        }
        return ans ;
    }
    int query(int l , int r)
    {
        return sum(r) - sum(l - 1) ;
    }
} bit ;
//  子树中大于当前节点  子树中小于当前节点
int minn[200005];
int dfn[200005], now = 0, size[200005], pa[200005];
ll ans = 0;
void dfs2()
{
    for (int i = 1 ; i <= n ; i ++)
    {
        //1 left , 2 right
        ll xi = 0 , yi = 0 , xo = 0 , yo = 0;
        for (int j = head[i] ; j != - 1 ; j = edge[j].next)
        {
            int v = edge[j].t;
            if(v != pa[i])
            {
                int x = minn[v];
                if (p[2] == 1) ans += 1ll * yi * (size[v] - x);
                else if (p[2] == 2) ans += 1ll * xi * (size[v] - x) + 1ll * yi * x;
                else ans += 1ll * xi * x;
                xi += x;
                yi += size[v] - x;
            }
        }
        xo = i - 1 - xi;
        yo = n - i - yi;
        if(p[2] == 1) ans += 1ll * yi * yo;
        else if(p[2] == 2) ans += 1ll * xi * yo + 1ll * yi * xo;
        else ans += 1ll * xi * xo;
    }
}
void Init()
{
    ans = 0, now = 0;
    mem(dfn,0);
    mem(minn,0);
    mem(size,0);
    mem(pa,0);
    mem(head,-1);
    cnt = 0;
}
void add (int f, int t)
{
    edge[cnt].t = t;
    edge[cnt].next = head[f];
    head[f] = cnt ++;
}
void dfs1(int root , int fa)
{
    size[root] = 1;
    dfn[root] = ++ now;
    for (int i = head[root]; i != -1 ; i = edge[i].next)
    {
        int v = edge[i].t;
        if(v != fa)
        {
            pa[v] = root;
            dfs1(v, root);
            size[root] += size[v];
        }
    }
}
void Deal()
{
    for (int i = 1 ; i <= n ; i ++)
    {
        for (int j = head[i] ; j != -1 ; j = edge[j].next)
        {
            int v = edge[j].t;
            if(v != pa[i])
                minn[v] = bit.query(dfn[v], dfn[v] + size[v] - 1);
        }
        bit.add(dfn[i],1);
    }
    for (int i = 1 ; i <= n ; i ++)
        bit.tree[i] = 0 ;
}
int main(void)
{
    scanf("%d", &T);
    while (T --)
    {
        scanf("%d", &n);
        Init();
        for (int i = 1 ; i <= 3 ; i ++)
            scanf ("%d", &p[i]);
        for (int i = 1 ; i < n ; i ++)
        {
            int u , v ;
            scanf("%d %d",&u , &v);
            add (u, v);
            add (v, u);
        }
        dfs1(1,1);
        Deal();
        dfs2();
        printf("%lld\n",ans);
    }

}
/*
2
5
1 2 3
3 1
3 5
1 2
1 4
 */

题目链接:https://www.codechef.com/problems/CHGORAM

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
拓扑排是一种对有向无环图进行排的算法,它可以用来确定前驱关系,验证图是否有环等。拓扑排的基本思想是将图节点按照一定的顺排列,使得所有的有向边从排在前面的节点指向排在后面的节点。在实际应用,拓扑排常常被用来解决任务调度、编译顺等问题。 在C++,可以使用DFS算法进行拓扑排。具体实现方法是,对于每个节点,先将其标记为正在访问,然后遍历其所有的邻居节点,如果邻居节点已经被访问过了,就说明存在环,返回false;如果邻居节点还没有被访问过,就递归地对其进行访问。当所有的邻居节点都被访问完毕后,将该节点标记为已经访问过,并将其加入到拓扑。最后,将拓扑列倒输出即可。 下面是一个使用DFS算法进行拓扑排的C++代码示例: ``` const int maxn = 100; int c[maxn]; int topo[maxn], t, n; int G[maxn][maxn]; bool dfs(int u) { c[u] = -1; for(int v = 0; v < n; v++) { if(G[u][v]) { if(c[v] < 0) return false; else if(!c[v] && !dfs(v)) return false; } } c[u] = 1; topo[--t] = u; return true; } bool topoSort() { t = n; memset(c,0,sizeof(c)); for(int u = 0; u < n; u++){ if(!c[u] && !dfs(u)) return false; } return true; } int main() { char ch[100] = {'a', 'b', 'c', 'd'}; n = 4; G[0][1] = 1; G[2][1] = 1; G[3][2] = 1; if(!topoSort()){ printf("无法拓扑排\n"); return 0; } for(int i = 0; i < 4; i++){ printf("%c ", ch[topo[i]]); } printf("\n"); } ```

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值