标签:lceil Last cf1325 int sqrt dep dfs Ehab rceil
题意:
给定一个无向连通图,你需要解决以下两个问题之一:
- 1364D:
- 找出一个大小为 \(\lceil \frac k2\rceil\) 的独立点集
- 找出一个大小不超过 \(k\) 的环
- 1325F:
- 找出一个大小为 \(\lceil \sqrt n\rceil\) 的独立点集
- 找出一个不小于 \(\lceil \sqrt n\rceil\) 的环
独立点集中,任意两点没有边直接相连
保证没有重边和自环
思路:
- 1364D:
随便找一个不可分割的环(即环中任意不相邻的两点间没有路径),若此环大小不超过 \(k\) 则输出,否则隔一个输出。怎么找这种环呢?
考虑 dfs树,若某点有后向边(back edge),取端点最小的那条后向边即可。因为是无向图而且一旦找到后向边就结束了,所以没有前向边(都成了后向边)和横叉边
当然如果图中无环,那就是一棵树,隔层取点即可
一个性质,但对这题似乎没什么用:
设最小的环大小为 \(m\),则环中任意不相邻的两点间必没有边直接相连。
证明:设环中有不相邻的两点,它们在环中的距离为 \(d_1,d_2\),则 \(d_1+d_2=m\)
因为不相邻所以 \(d_1,d_2\ge 2\implies d_1,d_2\le m-2\)
若这两点间有边直接相连,那么就会形成新的环,大小为 \(d_1+1\le m-1\) 比最小的环还小,故矛盾。所以这两点间不可能有连边。证毕。
int st, ed; //环的起点终点
int p[N]; //父亲
int d[N]; //深度
void dfs(int u) {
if(!st) { //还没找到环,试着找一下
for(int v : G[u]) if(v != p[u])
if(d[v] > d[st]) st = v, ed = u;
}
for(int v : G[u]) if(!d[v]) //正常dfs
p[v] = u, d[v] = d[u] + 1, dfs(v);
}
void sol() {
cin >> n >> m >> k;
while(m--) {
int x, y; cin >> x >> y;
G[x].pb(y), G[y].pb(x);
}
d[1] = 1; dfs(1);
//输出答案比较繁琐,但没难度,建议别看
int need = (k+1)/2;
if(!st) { //树
cout << 1 << endl;
vector<int> tr[2]; //染色找答案
function<void(int,int,int)> f = [&](int u,int fa,int dep) {
tr[dep].pb(u);
for(int v : G[u]) if(v != fa)
f(v, u, dep^1);
};
f(1, 0, 0);
if(tr[1].size()>tr[0].size()) swap(tr[0],tr[1]);
for(int i = 0; need; i++)
cout << tr[0][i] << ' ', need--;
}
else {
vector<int> ring; ring.pb(ed); //找出环
while(ed != st) ring.pb(ed=p[ed]);
if(ring.size() <= k) { //环大小不超过k
cout << 2 << endl << ring.size() << endl;
for(int i : ring) cout << i << ' ';
}
else {
cout << 1 << endl;
for(int i = 0; need; i += 2)
cout << ring[i] << ' ', need--;
}
}
}
- 1325F:
考虑 dfs树,如果一个点 \(u\) 有不少于 \(\lceil \sqrt n\rceil-2\) 条后向边(注意连父亲的不算),那么必有一条后向边的终点 \(v\) 使得 \(dep_u-dep_v+1\ge \lceil \sqrt n\rceil\),即找到一个大于等于 \(\lceil \sqrt n\rceil\) 的环。
否则,一定能找出独立点集。可以用下面两种方法之一说明
法一:每个点最多有 \(\lceil \sqrt n\rceil-3\) 条后向边。每次选一个未被ban的点 \(u\),满足 \(u\) 的子树中的点要么被选了要么被ban了。现在选 \(u\),那么 \(u\) 的父亲和所有后向边的终点会被ban,于是每选一个点最多会使 \(\lceil \sqrt n\rceil-2\) 个点被ban,所以能找到至少 \(\lceil \frac{n}{\lceil \sqrt n\rceil-1}\rceil\ge \lceil \sqrt n\rceil\) 个独立点。为了写码方便,从每个dfs分支中最深的点选起即可
法二:对 \(u\) 的所有后向边,不存在 \(dep_u-dep_v+1 = \lceil \sqrt n\rceil\) 即 \(dep_u-dep_v=\lceil \sqrt n\rceil-1\) 的点。那么可以按深度对 \(\lceil \sqrt n\rceil-1\) 的余数把所有点划分为同余集。由抽屉原理,必有一集合大小不小于 \(\lceil \frac{n}{\lceil \sqrt n\rceil-1}\rceil\)
int p[N], d[N], st, ed; //环的起点终点
bool ban[N]; //不能选
void dfs(int u) {
for(int v : G[u]) if(v != p[u] && d[v]) //找环
if(d[u]-d[v]+1 >= k) st = v, ed = u;
for(int v : G[u]) if(!d[v]) //正常dfs
p[v] = u, d[v] = d[u] + 1, dfs(v);
if(!ban[u]) for(int v : G[u]) ban[v] = true; //选独立点集
}
void sol() {
cin >> n >> m;
while(m--) {
int x, y; cin >> x >> y;
G[x].pb(y), G[y].pb(x);
}
k = ceil(sqrt(n))+0.5;
d[1] = 1; dfs(1);
//输出答案
if(st) {
cout << 2 << endl << d[ed]-d[st]+1 << endl;
cout << ed; //找出环
while(ed != st) cout << ' ' << (ed=p[ed]);
}
else {
cout << 1 << endl;
for(int i = 1; k; i++) if(!ban[i])
cout << i << ' ', k--;
}
}
标签:lceil,Last,cf1325,int,sqrt,dep,dfs,Ehab,rceil 来源: https://www.cnblogs.com/wushansinger/p/16469558.html
本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享; 2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关; 3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关; 4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除; 5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。