22牛客多校day1 J - Serval and Essay 启发式合并

J - Serval and Essay

题目描述

Serval is a new student in Japari Kindergarten.

There is an English course in the kindergarten. The teacher sets some writing tasks for students to improve their writing skills. Therefore, Serval has to complete an essay, in English.

You might know that in an essay, the author has to convince readers of several arguments by showing them evidence.

Serval have collected nn available arguments numbered from 1 1 1 to n n n that can be written in his essay. For the i i i-th argument, Serval can conclude that ii-th argument is true when the arguments numbered a a a i , 1 {i,1} i,1 , a a a i , 2 {i,2} i,2 , … , , … , ,, a a a i , k i {i,k_i} i,ki are all true. Specially, the i i i-th argument cannot be proven true by making conclusion when k i = 0. k_i = 0. ki=0. It is guaranteed that a i , j a_{i,j} ai,j ≠ i \neq i =i for all i i i ( 1 ≤ i ≤ n ) (1\leq i\leq n) (1in) , and a i , j ≠ a i , k a_{i,j}\neq a_{i,k} ai,j=ai,k when j ≠ k . j\neq k. j=k.

At the beginning of his essay, Serval will set exactly one argument from all the arguments as the argument basis, which is regarded as true. Starting with the argument basis, Serval will claim that some other arguments are true by making conclusions to complete his essay. It can be shown that for the i i i-th argument with k i = 0 k_i = 0 ki=0, it can be true if and only if it is the argument basis.

Serval wants to maximize the number of true arguments in the essay, so he needs to set the argument basis optimally. However, as a kindergarten student, he cannot even find out the number of true arguments he can obtain.

Could you help him find out the answer?

输入描述

Each test contains multiple test cases.

The first line contains an integer T T T ( 1 ≤ T ≤ 1 0 5 ) (1 \leq T \leq 10^5 ) (1T105) — the number of test cases.

For each test case:

The first line contains a single integer n n n ( 1 ≤ n ≤ 2 × 1 0 5 ) (1 \leq n \leq 2×10^5 ) (1n2×105)— the number of the available arguments.

For the i i i-th line in the following n n n lines, there is an integer k i k_i ki ( 0 ≤ k i < n ) (0\le k_{i} <n) (0ki<n) followed by k i k_i ki integers a a a i , 1 {i,1} i,1 , a a a i , 2 {i,2} i,2 , … , , … , ,, a a a i , k i {i,k_i} i,ki ( 1 ≤ a i , j ≤ n , a i , j ≠ i ) (1\le a_{i,j}\le n,a_{i,j}\neq i) (1ai,jn,ai,j=i) — the arguments required to make the conclusion for the i i i-th argument. It is guaranteed that a i , j ≠ i a_{i,j}\neq i ai,j=i for all i i i ( 1 ≤ i ≤ n ) (1\leq i\leq n) (1in), and a i , j ≠ a i , k a_{i,j}\neq a_{i,k} ai,j=ai,k when j ≠ k j\neq k j=k.

It is guaranteed that the sum of n n n in all the test cases does not exceed 2 × 1 0 5 2×10^5 2×105 and the sum of k i k_i ki in all the test cases does not exceed 5 × 1 0 5 5×10^5 5×105.

输出描述

For the i i i-th test case, print a line containing “Case # i i i : x x x” (without quotes), where i i i is the number of the test case starting from 1 1 1 and x x x is the answer to the test case, which is the maximum number of true arguments when setting the argument basis optimally.

输入样例:

3
4
0
1 1
2 1 2
2 2 3
5
1 3
1 1
1 2
1 5
4 1 2 3 4
7
0
2 1 4
1 2
1 3
2 3 4
1 1
2 5 6

输出样例:

Case #1: 4
Case #2: 3
Case #3: 4

样例解释

For the first sample, Serval can set the 1 1 1-st argument as the argument basis to obtain 4 4 4 true arguments in the essay. The following process shows how Serval makes conclusions in his essay.

⋅ Set the 1 1 1-st argument as the argument basis, which is regarded as true.
⋅ Conclude that 2 2 2-nd argument is true because 1 1 1-st argument is true.
⋅ Conclude that 3 3 3-rd argument is true because both 1 1 1-st and 2 2 2-nd arguments are true.
⋅ Conclude that 4 4 4-th argument is true because both 2 2 2-nd and 3 3 3-rd arguments are true.

题意

给定一张有向无环图,有 n n n 个点均为白色,开局可以选定一个点将其染成黑色,若一个点的前置节点均为黑色,那么这个点也可以染成黑色。求最多能染成黑点的个数。

思路

对于入度为 1 1 1 的节点,与其选这个节点不如选她的父节点,因为将其父节点染黑后她的前置节点全为黑色,这个节点也能染黑。于是可以将这两个节点合并,子节点原本的出边也要合并到父节点上。考虑到这两点可能存在的公共边需要减少存在公共出边的节点的入度。若减少入度之后的子节点的入度变为了 1 1 1 则又可以重复上述的合并操作。
最终会得到若干个连通块,答案便是 s i z e size size 最大的连通块的 s i z e size size
维护连通块以及连通块的 s i z e size size 用并查集即可。
对于合并操作需要合并两个集合的边,在最坏情况下需要合并 n − 1 n-1 n1 次,若是将两个集合的边都遍历一遍去判断重边复杂度会达到 O ( m 2 ) O(m^2) O(m2) 显然是不允许的。此时合并操作便可以用启发式合并去做,启发式合并对于两个集合合并的操作,每次将 s i z e size size 小的集合合并到 s i z e size size 更大的集合中去。易证 启发式合并的时间复杂度为 O ( n l o g n ) O(nlogn) O(nlogn) 对于每个元素所处的集合的每次合并都会使得集合的大小至少乘以2,故一个集合最多被合并 l o g 2 n log_2n log2n 次。故我们将每个集合的出边存到一个 s e t set set 中,每次将 s i z e size size 小的集合的边插入到 s i z e size size 大的集合的 s e t set set 中即可。而且 s e t set set 会自动去重,这样复杂度乘上 s e t set set l o g log log 可以做到 O ( m l o g 2 m ) O(mlog^2m) O(mlog2m)

Code

#include<bits/stdc++.h>
using namespace std;
#define __T int csT;scanf("%d",&csT);while(csT--)
#define endl '\n'
const int mod=998244353;
const double PI= acos(-1);
const double eps=1e-6;
const int N=2e5+7;


int n,k,v,ans;
int fa[N],sz[N];
set<int> to[N],fr[N];

int find(int x)
{
    if(fa[x]==x)return x;
    return fa[x]=find(fa[x]);
}
void merge(int x,int y)
{
    x=find(x);
    y=find(y);
    if(x==y)return;//若两个集合已经合并就return
    if(to[x].size()>to[y].size())swap(x,y);//默认x为小的集合
    fa[x]=y;
    sz[y]+=sz[x];
    vector<pair<int,int>> vc;
    for(set<int>::iterator it=to[x].begin();it!=to[x].end();++it)
    {
        to[y].insert(*it);//将x的出边加到y的出边
        fr[*it].erase(x);//删除原本x出边指向点的入边
        fr[*it].insert(y);//加上x出边指向点y的入边
        if(fr[*it].size()==1)
        {
            vc.push_back({*it,y});
        }
    }
    for(int i=0;i<vc.size();++i)
    {
        merge(vc[i].first,vc[i].second);
    }
}

int cs;
int main()
{
    cs=0;
    __T
    {
        scanf("%d",&n);
        for(int i=1;i<=n;++i)
        {
            fa[i]=i;
            sz[i]=1;
            to[i].clear();
            fr[i].clear();
        }
        for(int i=1;i<=n;++i)
        {
            scanf("%d",&k);
            for(int j=1;j<=k;++j)
            {
                scanf("%d",&v);
                to[v].insert(i);
                fr[i].insert(v);
            }
        }//对于每个点用set存入边和出边
        for(int i=1;i<=n;++i)
        {
            if(fr[i].size()==1)//入边为1意味着可以合并
            {
                merge(i,*fr[i].begin());
            }
        }
        ans=0;
        for(int i=1;i<=n;++i)
        {
            ans=max(ans,sz[i]);
        }
        printf("Case #%d: %d\n",++cs,ans);
    }
    return 0;
}
  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

柯西可乐

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值