Codeforces Round #738 (Div. 2) D2题解

时间:2021-08-17
本文章向大家介绍 Codeforces Round #738 (Div. 2) D2题解,主要包括 Codeforces Round #738 (Div. 2) D2题解使用实例、应用技巧、基本知识点总结和需要注意事项,具有一定的参考价值,需要的朋友可以参考一下。

D2. Mocha and Diana (Hard Version)

至于D1,由于范围是1000,我们直接枚举所有的边,看看能不能加上去就行,复杂度是\(O(n^2logn)\).至于\(n\)到了\(1e5\),就要重新考虑解法。
考虑到树的边数是\(n-1\)。也就是说我们枚举的大多数边都是无效的,这个时候就要考虑我们能不能只连有效的边,对于两个连通块而言,显然在这两个连通块之间我们只需要连一条边就可以了。还有一般这种两个图的题,一般都是在一个图中操作,在另一个图中进行合不合法的判定。这里我们以第一个图作为操作的对象,以第二个图作为判定的依据。首先想到的就是我们先将两个图都划分连通块,然后枚举第一个图任意两个连通块x,y,考虑两个连通块的点,若存在u属于x,v属于y并且u和v在图二中不属于同一个连通块,那么u和v就可以相连,这两个连通块就可以联通。并且两个连通块不能连的充要条件是这两个连通块内的所有点在图二中都属于同一个连通块内。枚举任意两个连通块显然复杂度是过不去的,我们能不能固定一个连通块(比如说连通块1),然后尝试将其他连通块与它联通,但这样会不会少连一些边?比如连通块x与连通块y都不能与1联通,但这两个能不能联通呢?考虑下刚才提到的充要条件,这两个连通块不能和1联通,说明这两个连通块内的所有点和1的所有点在图二中都属于同一个连通块内。那这两个连通块的点在图二也都属于同一个连通块内啊!肯定不能连啊。好了,那我们固定一号连通块,尝试将其他的所有连通块与它联通。显然我们找点的时候我们肯定不能枚举所有的点来判断,既然要求找到一个点对即可。我们考虑保存下当前一号连通块内的所有点在图二中的连通块编号,以及该编号中的其中一个点(方便输出方案)。当下一个连通块来的时候我们直接枚举这个连通块内的所有点来判断是否有没有符合要求的点对。并且当可以联通时,我们还需要在图二中连边,合并两个连通块,这个可以用并查集实现,但有时这两个连通块(符合要求的点对在图二中的两个连通块)都在我们维护的一号连通块内(图一中),这就需要我们维护的东西支持删除操作,这个我用了set,不想再动脑了。你以为这就结束了吗?不不不....我们发现当一号连通块内对应的在图二中的连通块个数越多,越容易与其它连通块联通,为了避免一些恶心情况(因为当前1号连通块对应的图二中的连通块个数为1,而失败联通,但之后有其他的连通块对应图二有更多的连通块,调整下顺序就能联通原本不能联通的),我们将图一中的连通块按照在图二中对应地连通块的个数排序,个数多着在前,这样我们就先尽可能的增加我们维护的一号图在二号图中的连通块的个数,之后就能尽可能多的接纳其他的连通块。总复杂度\(O(nlogn)\),多少次想放弃,但还是想坚持下去....

查看代码

//不等,不问,不犹豫,不回头.
#include
#define _ 0
#define ls p<<1
#define db double
#define rs p<<1|1
#define P 1000000007
#define ll long long
#define INF 1000000000
#define get(x) x=read()
#define PLI pair
#define PII pair
#define ull unsigned long long
#define put(x) printf("%d\n",x)
#define putl(x) printf("%lld\n",x)
#define rep(x,y,z) for(int x=y;x<=z;++x)
#define fep(x,y,z) for(int x=y;x>=z;--x)
#define go(x) for(int i=link[x],y=a[i].y;i;y=a[i=a[i].next].y)
using namespace std;
const int N=1e5+10;
int n,m1,m2,f[3][N],vis[N],num,bx[N],by[N],id[N];
struct wy{int len,v;}a[N];
vectorv[N];
mapmp;
sets;

inline int read()
{
int x=0,ff=1;
char ch=getchar();
while(!isdigit(ch)) {if(ch=='-') ff=-1;ch=getchar();}
while(isdigit(ch)) {x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}
return x*ff;
}

inline int getf(int op,int x){return f[op][x]==x?x:f[op][x]=getf(op,f[op][x]);}

inline void init()
{
get(n);get(m1);get(m2);
rep(i,1,n) f[1][i]=f[2][i]=i;
rep(i,1,m1)
{
int get(x),get(y);
int t1=getf(1,x),t2=getf(1,y);
if(t1!=t2) f[1][t1]=t2;
}
rep(i,1,m2)
{
int get(x),get(y);
int t1=getf(2,x),t2=getf(2,y);
if(t1!=t2) f[2][t1]=t2;
}
}

inline bool cmp(wy a,wy b) {return a.len>b.len;}

inline void prework()
{
rep(i,1,n) f[1][i]=getf(1,i),f[2][i]=getf(2,i);
rep(i,1,n)
{
if(!vis[f[1][i]])
{
vis[f[1][i]]=1;
id[f[1][i]]=++num;
}
a[id[f[1][i]]].len++;
a[id[f[1][i]]].v=f[1][i];
}
sort(a+1,a+num+1,cmp);
rep(i,1,num) id[a[i].v]=i;
rep(i,1,n) v[id[f[1][i]]].push_back(i);
memset(vis,0,sizeof(vis));
for(auto x:v[1])
{
if(vis[f[2][x]]) continue;
vis[f[2][x]]=1;
s.insert(f[2][x]);
mp[f[2][x]]=x;
}
}

inline void solve()
{
int ans=0;
rep(i,2,num)//枚举从第二个连通块开始的所有连通块,尝试与第一个连通块合并。
{
bool flag=false;
for(auto x:s)//枚举每个连通块
{
if(flag) break;
for(auto y:v[i])
{
int t=getf(2,y);
if(t!=x&&!flag)
{
bx[++ans]=mp[x];
by[ans]=y;
f[2][t]=x;
if(s.find(t)!=s.end()) s.erase(t);
flag=true;
}
else if(flag&&!vis[t])
{
vis[t]=1;
s.insert(t);
mp[t]=y;
}
}
}
}
put(ans);
rep(i,1,ans) printf("%d %d\n",bx[i],by[i]);
}

int main()
{
// freopen("1.in","r",stdin);
//freopen("sol.out","w",stdout);
init();
prework();
solve();
return (0_0);
}
//以吾之血,铸吾最后的亡魂.

原文地址:https://www.cnblogs.com/gcfer/p/15152888.html