1 条题解

  • 0
    @ 2026-8-20 14:59:04

    题目传送门

    当一个节点的出度大于等于 22 时,它的子节点会构成一个团。

    不难注意到,一个团所能到达的所有的点,都可以被拉进这个团中。

    所以可以从每个团 DFS,找出这个团所能到达的所有点,拉进这个团中。

    但是直接建边维护团时空复杂度都是 O(n2)O(n^2),无法通过,因此我们要用并查集维护团。

    统计答案还是很简单的,设某个团的大小为 ss,则这个团中的边数 w=s(s1)w=s(s-1)w\sum w 加上团外的边数就是答案。

    :::success[AC Code]

    #include<bits/stdc++.h>
    #define int long long
    using namespace std;
    const int N=1e5+5;
    int n,m,f[N],s[N],ans;
    vector<int>ed[N];
    bool vis[N];
    void add(int u,int v){ed[u].push_back(v);}
    int find(int x){return f[x]==x?x:f[x]=find(f[x]);}
    void merge(int x,int y){x=find(x),y=find(y);if(x!=y)f[x]=y,s[y]+=s[x];}
    void dfs(int u,int f){
    	vis[u]=1,merge(u,f);
    	for(auto v:ed[u]){
    		merge(v,f);
    		if(!vis[v])
    			dfs(v,f);
    	}
    }
    signed main(){
    	cin>>n>>m;
    	for(int i=1;i<=n;i++)
    		f[i]=i,s[i]=1;
    	for(int i=1,u,v;i<=m;i++)
    		cin>>u>>v,add(u,v);
    	for(int u=1;u<=n;u++)
    		for(int i=1;i<ed[u].size();i++)
    			merge(ed[u][i],ed[u][i-1]);
    	for(int u=1;u<=n;u++)
    		if(find(u)!=u)
    			add(find(u),u);
    	for(int u=1;u<=n;u++)
    		if(ed[u].size()>1)
    			for(auto v:ed[u])
    				if(!vis[v])
    					dfs(v,v);
    	for(int u=1;u<=n;u++)
    		for(auto v:ed[u])
    			if(find(u)!=find(v))
    				ans++;
    	for(int u=1;u<=n;u++)
    		if(f[u]==u)
    			ans+=s[u]*(s[u]-1);
    	cout<<ans;
    	return 0;
    }
    

    :::

    • 1

    [JOISC 2014] 有趣的交朋友 / Making Friends is Fun

    信息

    ID
    4676
    时间
    1000ms
    内存
    256MiB
    难度
    9
    标签
    递交数
    13
    已通过
    2
    上传者