You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

188 lines
4.7 KiB

2 years ago
# $Aimai$ $Trip$
### 题目描述
有 $n$ 只猫猫在玩捉迷藏。猫猫们的编号分别为 $1,2,\cdots,n$。
屋子里有 $k$ 个房间,**每个房间只能容纳一只猫猫**。
每个房间有一定的属性,可以用 $x_i,y_i$ 来表示,意为:
* 这个房间要么空着,要么容纳第 $x_i$ 只猫猫,要么容纳第 $y_i$ 只猫猫。
现在云浅可以买下前 $m$ 个房间给猫猫们使用。云浅想要知道,$m$ 的值至少是多少,才能容纳所有猫猫。**保证买下所有房间后一定可以容纳所有猫猫。**
为了更好地帮助小可爱,你还需要输出一个方案。如果有多个可行的方案,输出任意一个即可。
### 输入格式
第一行两个正整数 $n,k$。
第二行 $k$ 个正整数 $x_1,\cdots,x_k$,表示每个房间的第一种属性。
第三行 $k$ 个正整数 $y_1,\cdots,y_k$,表示每个房间的第二种属性。
### 输出格式
先输出一行一个正整数 $m$ 表示答案。
第二行输出 $m$ 个正整数,以空格隔开。其中,若第 $i$ 个数为 $0$ 则表示第 $i$ 个房间空着不用,第 $i$ 个数为 $1$ 则表示分给第 $x_i$ 只猫猫,第 $i$ 个数为 $2$ 则表示分给第 $y_i$ 只猫猫。
### 数据范围
对于 $100\%$ 的数据,$1\le n \le 10^5,1\le k\le 2\times 10^5,1\le x_i,y_i\le n,x_i\neq y_i$。
| 测试点编号 | $n$ | $k$ |
| :---------: | :--------: | :----------------: |
| $1\sim 6$ | $\le 20$ | $\le 20$ |
| $7\sim 10$ | $\le 1000$ | $\le 1000$ |
| $11\sim 14$ | $\le 1000$ | $\le 10^5$ |
| $15\sim 20$ | $\le 10^5$ | $\le 2\times 10^5$ |
<div style="page-break-after: always"></div>
### 题解
首先二分答案,相当于只需要判定一下是否所有小可爱都能住进至少一个房间。
考虑对每个房间连边 $(x_i,y_i)$,那么,所有小可爱都能住进至少一个房间,当且仅当:
* 图中的每个连通块的边数均不小于点数。
证明:若要求所有小可爱都能住进至少一个房间,显然边数必然不小于点数。
如果边数不小于点数,那么这个连通块中至少存在一个环。考虑令在环上的边所代表的房间依次给第 $x_i$ 只小可爱,然后从这个环向外延伸出去若干条链,链上的边从环内指向环外即可。
构造方案也可以使用类似的方法。
于是就做完了,时间复杂度 $O((n+k)\log k)$。
#### 参考代码
```c++{.line-numbers}
#include<bits/stdc++.h>
#define int long long
using namespace std;
inline int read(){
int x=0,f=1;char c=getchar();
for(;(c<'0'||c>'9');c=getchar()){if(c=='-')f=-1;}
for(;(c>='0'&&c<='9');c=getchar())x=x*10+(c&15);
return x*f;
}
const int MN=2e5+5;
int n,k;
int fr[MN],to[MN];
struct Edge{
int to,id;
Edge(int T,int I):to(T),id(I){}
Edge(){}
};
vector<Edge>G[MN];
vector<int>S;
bool vis[MN];
void getnodes(int u){
vis[u]=1;S.push_back(u);
for(auto e:G[u]){
int v=e.to,i=e.id;
if(!vis[v])getnodes(v);
}
}
vector<int>C;
int fa[MN],pre[MN],res[MN];
bool fi=0;
void findcycle(int u){
vis[u]=1;
for(auto e:G[u]){
int v=e.to,i=e.id;
if(i==pre[u])continue;
if(!vis[v]){fa[v]=u,pre[v]=i,findcycle(v);continue;}
if(fi)continue;
int x=u;
while(x!=v){
C.push_back(x),res[pre[x]]=(fr[pre[x]]==x?2:1);
x=fa[x];
}
res[i]=(fr[i]==u?1:2);fi=1;C.push_back(v);
}
}
bool chk(int m){
for(int i=1;i<=n;i++)G[i].clear();
memset(vis,0,sizeof(vis)),memset(pre,0,sizeof(pre)),memset(fa,0,sizeof(fa));
for(int i=1;i<=m;i++){
int u=fr[i],v=to[i];
G[u].push_back(Edge(v,i)),G[v].push_back(Edge(u,i));
}
for(int i=1;i<=n;i++){
if(vis[i])continue;
fi=0,C.clear();findcycle(i);
if(!fi)return 0;
}
return 1;
}
void dfs(int u){
vis[u]=1;
for(auto e:G[u]){
int v=e.to,i=e.id;
if(vis[v]||res[i]!=0)continue;
res[i]=(fr[i]==v?1:2);dfs(v);
}
}
void getans(int m){
for(int i=1;i<=n;i++)G[i].clear();
memset(vis,0,sizeof(vis)),memset(pre,0,sizeof(pre));
memset(fa,0,sizeof(fa)),memset(res,0,sizeof(res));
for(int i=1;i<=m;i++){
int u=fr[i],v=to[i];
G[u].push_back(Edge(v,i)),G[v].push_back(Edge(u,i));
}
for(int i=1;i<=n;i++){
if(vis[i])continue;
S.clear();getnodes(i);fi=0;
for(int p:S)vis[p]=0;
C.clear();findcycle(i);
for(int p:S)vis[p]=0;
for(int p:C)vis[p]=1;
for(int p:C)dfs(p);
for(int p:S)vis[p]=1;
}
for(int i=1;i<=m;i++)cout<<res[i]<<" ";puts("");
}
signed main(void){
freopen("cats.in","r",stdin);
freopen("cats.out","w",stdout);
n=read(),k=read();
for(int i=1;i<=k;i++)fr[i]=read();
for(int i=1;i<=k;i++)to[i]=read();
int L=1,R=k,ans=L;
while(L<=R){
int mid=(L+R)>>1;
if(chk(mid))R=mid-1,ans=mid;
else L=mid+1;
}
cout<<ans<<endl;
getans(ans);
return 0;
}
```
<div style="page-break-after: always"></div>