ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

Codeforces Round #738 (Div. 2)

2021-08-17 12:04:03  阅读:156  来源: 互联网

标签:sub limits int sum Codeforces 738 Div include size


Codeforces Round #738 (Div. 2)

CF 上蓝祭。

\(A\)

看错题后发现可以用无限次与运算。

显然与的越多,结果只会单调不升,而每相邻两个操作一次就可以达到全都与一遍。

那么 \(Ans=a_1\&a_2\&\cdots\&a_n\)。

\(B\)

发现 \(R,B\) 交错填写一定是最优的,唯一需要讨论的是起始位置,根据第一个有颜色的格子讨论即可。

\(C\)

每个经过点恰好一次,简单分类讨论即可。

\(D1\&D2\)

给定两个点一一对应的森林,连边可能不同,要求在两个图均仍为森林的前提下,加尽量多的边。

\(D1\) 的范围可以支持 \(O(n^2)\) 的算法,那么只需要枚举所有可能连边。能连就连。

这个贪心的正确性不难证明,因为每条边的贡献都是一,而且顺序无关。

对于 \(D2\) 则要求使用更高级的方法模拟上述过程。

可以利用 set 记录 \(1\) 中每个连通块与 \(2\) 中的哪些连通块对应,然后考虑 \(1\) 所有中的连通块。

每次连边相当于是合并两个 \(1\) 中的连通块和两个 \(2\) 中的连通块,不失一般性,假设 \(1\) 的连通块数量 \(<\) \(2\) 的。

如果存在 \(>1\) 个元素的行 set,那么它一定可以和任意一个连通块合并。

否则,因为 \(1\) 的连通块个数 \(<\) \(2\) 的,且每个连通块都只对应一个 \(2\) 的连通块,所以每个 \(1\) 对应的都不同。

那么同样可以任取两个合并,直接使用启发式合并,删除 + 合并即可。

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<set>
#include<map>
#include<vector>
using namespace std;

const int N = 1e5 + 10;
int n, m[2], fa[2][N];
set<int> s[2][N];
set<int>::iterator it;
set<pair<int, int> > sub; 
map<int, int> mp[N];
vector<pair<int, int> > ans;

int read(){
	int x = 0, f = 1; char c = getchar();
	while(c < '0' || c > '9') f = (c == '-') ? -1 : 1, c = getchar();
	while(c >= '0' && c <= '9') x = x * 10 + c - 48, c = getchar();
	return x * f;
}

int Get(int t, int x){
	if(fa[t][x] == x) return x;
	return fa[t][x] = Get(t, fa[t][x]);
}

void Merge(int t, int x, int y){
	for(it = s[t][y].begin(); it != s[t][y].end(); it ++){
		int a = * it;
		s[t][x].insert(a);
		s[t ^ 1][a].erase(y);
		s[t ^ 1][a].insert(x);
		if(!t)
			mp[x][a] = mp[y][a];
		else
			mp[a][x] = mp[a][y];
	}
}

int main(){
	n = read(), m[0] = read(), m[1] = read();
	for(int i = 1; i <= n; i ++) fa[0][i] = fa[1][i] = i;
	for(int t = 0; t < 2; t ++)
		for(int i = 1; i <= m[t]; i ++){
			int u = read(), v = read();
			fa[t][Get(t, u)] = Get(t, v);
		}
	if(m[0] < m[1]) swap(fa[0], fa[1]);
	
	for(int i = 1; i <= n; i ++){
		int u = Get(0, i), v = Get(1, i);
		s[0][u].insert(v);
		s[1][v].insert(u);
		mp[u][v] = i;
	}
	for(int i = 1; i <= n; i ++)
		if(fa[0][i] == i) sub.insert(make_pair(- s[0][i].size(), i));
	
	while(sub.size() > 1){
		int x = sub.begin() -> second; sub.erase(sub.begin());
		int y = sub.begin() -> second; sub.erase(sub.begin());
		if(s[0][x].size() < s[0][y].size()) swap(x, y);
		int a = * s[0][x].begin();
		int b = * s[0][y].begin();
		if(a == b){it = s[0][x].begin(); it ++; a = * it;}
		ans.push_back(make_pair(mp[x][a], mp[y][b]));
		if(s[1][a].size() < s[1][b].size()) swap(a, b);
		Merge(0, x, y);
		Merge(1, a, b);
		sub.insert(make_pair(- s[0][x].size(), x));
	}
	
	printf("%d\n", ans.size());
	for(int i = 0; i < (int) ans.size(); i ++)
		printf("%d %d\n", ans[i].first, ans[i].second);
	return 0;
}

\(E\)

要求构造长度为 \(n\) 的序列,要求:

  1. \(L_i\leq a_i\leq R_i\)
  2. \(\sum a_i\leq M\)
  3. \(\gcd(a_1,a_2,\dots,a_n)=1\)

求方案数。

莫比乌斯反演,设 \(g(a_1,a_2,\dots,a_n)\) 为真当且仅当它满足前两个条件。

\[Ans=\sum\limits_{a_1=L_1}^{R_1}\sum\limits_{\cdots}\sum\limits_{a_n=L_n}^{R_n}[\gcd(a_1,a_2,\dots,a_n)=1]g(a_1,a_2,\dots,a_n) \]

\[=\sum\limits_{a_1=L_1}^{R_1}\sum\limits_{\cdots}\sum\limits_{a_n=L_n}^{R_n}g(a_1,a_2,\dots,a_n)\sum\limits_{d|\gcd(a_1,a_2,\dots,a_n)} \mu(d) \]

\[=\sum\limits_{d=1}^m\mu(d)\sum\limits_{a_1=\lfloor \frac{L_1}{d}\rfloor}^{\lceil\frac{R_1}{d}\rceil}\sum\limits_{\cdots}\sum\limits_{a_1=\lfloor \frac{L_n}{d}\rfloor}^{\lceil\frac{R_n}{d}\rceil}g(a_1d,a_2d,\dots,a_nd) \]

后面的东东可以通过前缀和优化 DP 做到 \(O(nM/d)\) 计算。

总时间复杂度 \(O(nM\log M)\)。

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
 
typedef long long LL;
const int N = 1e5 + 10;
const LL P = 998244353;
 
int n, m, tot, l[55], r[55], prm[N];
LL mu[N], f[N], sum[N];
bool vis[N];
 
int read(){
	int x = 0, f = 1; char c = getchar();
	while(c < '0' || c > '9') f = (c == '-') ? -1 : 1, c = getchar();
	while(c >= '0' && c <= '9') x = x * 10 + c - 48, c = getchar();
	return x * f;
}
 
LL Get(int d){
	int M = m / d;
	f[0] = 1;
	for(int i = 1; i <= M; i ++) f[i] = 0;
	for(int i = 1; i <= n; i ++){
		int L = (l[i] + d - 1) / d, R = r[i] / d;
		if(L > R) return 0;
		for(int j = 0; j <= M; j ++) sum[j] = (f[j] + (j ? sum[j - 1] : 0)) % P;
		for(int j = 0; j <= M; j ++)
			f[j] = ((j - L >= 0 ? sum[j - L] : 0) - (j - R - 1 >= 0 ? sum[j - R - 1] : 0) + P) % P;
	}
	LL sum = 0;
	for(int i = 1; i <= M; i ++) sum = (sum + f[i]) % P;
	return sum;
} 
 
int main(){
	n = read(), m = read();
	for(int i = 1; i <= n; i ++) l[i] = read(), r[i] = read();
	mu[1] = 1;
	for(int i = 2; i <= m; i ++){
		if(!vis[i]) prm[++ tot] = i, mu[i] = P - 1;
		for(int j = 1; j <= tot && prm[j] * i <= m; j ++){
			vis[prm[j] * i] = true;
			if(i % prm[j]) mu[i * prm[j]] = (P - mu[i]);
			else break;
		}
	}
	LL ans = 0;
	for(int i = 1; i <= m; i ++) ans = (ans + mu[i] * Get(i)) % P;
	printf("%lld\n", ans);
	return 0;
}

标签:sub,limits,int,sum,Codeforces,738,Div,include,size
来源: https://www.cnblogs.com/lpf-666/p/15151683.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有