• LOJ3176「IOI2019」景点划分【分析性质,构造】


    给定 (n) 个点 (m) 条边的简单无向连通图和三个正整数 (a,b,c),求将点集划分为大小分别为 (a,b,c) 的集合 (A,B,C) 的方案,使得至少两个的导出子图连通。

    (nle 10^5)(mle 2cdot 10^5)(a+b+c=n)


    图连通性、构造 ( ightarrow) dfs 树。

    不妨设 (ale ble c),则有 (alefrac n3)(blefrac n2)(cgefrac n3),问题转化为选取两个大小至少为 (a,b) 的连通导出子图(更大了可以剥叶子)

    先看树的部分分怎么做:随便定个点 (1) 做根,其中一个连通子图可以是子树,那么只需要存在一个子树的大小 (in[a,n-a]) 就可以了(若子树大小 (ge b) 则子树对应大小 (ge b) 的,否则对应大小 (ge a) 的),构造方案就从割开的边向两边 dfs。

    然后看原题:如果 dfs 树有解那么就做完了,否则找到一个最深的大小 (>n-a) 的子树(可以发现这样的子树只有一个),设根为 (x),删掉 (x) 之后 (x) 的儿子有一些子树,其中有些可以到 (x) 的子树之外,有些不行,那么必定要能到 (x) 的子树之外的子树大小之和+(x) 的子树之外的大小至少是 (a),因为另一个连通子图只能占这些,否则跟树的情况类似,一定有解,构造方案仍然是直接 dfs:(x) 先把自家势力范围占据完,不够再占共用范围,剩下的给另一个连通子图。

    时间复杂度 (O(n+m))

    #include<bits/stdc++.h>
    #define MP make_pair
    #define PB emplace_back
    #define fi first
    #define se second
    using namespace std;
    typedef pair<int, int> pii;
    const int N = 100003, M = N<<2;
    template<typename T>
    void read(T &x){
    	int ch = getchar(); x = 0;
    	for(;ch < '0' || ch > '9';ch = getchar());
    	for(;ch >= '0' && ch <= '9';ch = getchar()) x = x * 10 + ch - '0';
    }
    template<typename T>
    bool chmin(T &a, const T &b){if(a > b) return a = b, 1; return 0;}
    int n, m, cnt, head[N], to[M], nxt[M];
    pii a[3];
    void add(int u, int v){to[++cnt] = v; nxt[cnt] = head[u]; head[u] = cnt;}
    int dfn[N], low[N], tim, siz[N], sum[N], rem, ans[N];
    void doit(int x, int col, bool flg){
    	if(!rem || ans[x]) return;
    	-- rem; ans[x] = col;
    	for(int i = head[x];i;i = nxt[i])
    		if(flg || dfn[to[i]] > dfn[x])
    			doit(to[i], col, flg);
    }
    void dfs(int x, int f){
    	siz[x] = sum[x] = 1;
    	dfn[x] = low[x] = ++tim;
    	for(int i = head[x];i;i = nxt[i]) if(to[i] != f){
    		if(!dfn[to[i]]){
    			dfs(to[i], x);
    			siz[x] += siz[to[i]];
    			if(low[to[i]] >= dfn[x])
    				sum[x] += siz[to[i]];
    			chmin(low[x], low[to[i]]);
    		} else chmin(low[x], dfn[to[i]]);
    	}
    	if(siz[x] >= a[0].fi){
    		if(sum[x] > n-a[0].fi){
    			for(int i = 1;i <= n;++ i)
    				printf("0 ");
    			exit(0);
    		}
    		if(sum[x] > n-a[1].fi) swap(a[0], a[1]);
    		rem = a[0].fi-1; ans[x] = a[0].se;
    		for(int i = head[x];i;i = nxt[i])
    			if(low[to[i]] >= dfn[x])
    				doit(to[i], a[0].se, false);
    		for(int i = head[x];i;i = nxt[i])
    			if(dfn[to[i]] > dfn[x] && low[to[i]] < dfn[x])
    				doit(to[i], a[0].se, false);
    		rem = a[1].fi; doit(1, a[1].se, true);
    		for(int i = 1;i <= n;++ i) printf("%d ", ans[i] ?: a[2].se);
    		exit(0);
    	}
    }
    int main(){
    	read(n); read(m);
    	for(int i = 0;i < 3;++ i){
    		read(a[i].fi); a[i].se = i+1;
    	}
    	sort(a, a+3);
    	for(int i = 0, u, v;i < m;++ i){
    		read(u); read(v);
    		add(++u, ++v); add(v, u);
    	}
    	dfs(1, 0);
    }
    
  • 相关阅读:
    Android 禁用以及捕捉home键
    android中正确导入第三方jar包
    使用SharedPreferences进行数据存储
    tomcat不安全因素
    spring边边角角
    宏定义
    C++变量对比java变量所占内存
    结构指针的分析
    对结构使用指针
    什么是程序文件?
  • 原文地址:https://www.cnblogs.com/AThousandMoons/p/14882280.html
Copyright © 2020-2023  润新知