成都信息工程大学第八届校赛 H J 题解

H. Bang Bang Keli Ba

题目大意

给定数组 \(a\) ,构造递增序列 \(b\) 和递减序列 \(c\)\(a_i=b_i+c_i\)

题解

下面证明解的存在性,存在性证明后,解也就出来了。
对于序列 \(b,c\) ,一个递增,一个递减就意味这 \(b\) 的差分数组 \(b'\) 每个元素都大于等于 \(0\)\(c\) 的差分数组 \(c'\) 每个元素都小于等于 \(0\) 。对于 \(a\) 的差分数组 \(a'\) ,我们同样有 \(a'_i=b'_i+c'_i\) ,于是对于 \(a'_i\) ,我们把它拆成一个非负数和一个非整数的和即可,显然是存在无数多的解的。

同时也有其他很多解法,在此不介绍了。

AC代码

#include <bits/stdc++.h>
#define rep(i,a,b) for(int i=a;i<b;i++)
using namespace std;
using ll = long long;

constexpr int N = 1e5+5, P = 1e9+7;
int a[N], b[N], c[N];

int main() {
	int n; cin>>n;
	rep(i,1,n+1)cin>>a[i];
	rep(i,1,n+1){
		int x = a[i] - a[i-1];
		if (x < 0) c[i] = x;
		else b[i] = x;
		b[i] += b[i-1];
		c[i] += c[i-1];
	}
	rep(i,1,n+1)cout<<b[i]<<" \n"[i==n];
	rep(i,1,n+1)cout<<c[i]<<" \n"[i==n];
	return 0;
}

J. No Idea

题目大意

\(n\) 个数中选至少 \(2\) 个数,使这几个数的 \(\gcd\)\(1\) ,问方案数。

题解

考虑容斥。定义函数 \(f(x)\)\(\gcd\)\(x\) 的方案数,那么答案就是 \(f(1)\) 。另外再定义一个函数 \(g(x)\) ,表示 \(\gcd\)\(x\) 的倍数的方案数。根据定义,我们有

\[g(x)=f(x)+f(2x)+f(3x)+... \]

移项得

\[f(x)=g(x)-f(2x)-f(3x)-... \]

于是我们就可以想出这么一种解法:从大到小开始算,假设现在算到了 \(x\),我们先算出 \(g(x)\) ,那么
\(f(x)\) 就可以由上式解出。下面考虑如何计算 \(g(x)\) .

考虑这么一个组合问题:从 \(n\) 个不同的球里选不少于 \(2\) 个球的方案数。答案即为

\[\binom{n}{2}+\binom{n}{3}+...+\binom{n}{n}=2^n-\binom{n}{0}-\binom{n}{1}=2^n-1-n \]

具体的实现方法为,用一个数组记录每个数字出现了多少次(因为数的大小最多只有 \(2\times 10^5\)),然后从 \(2\times 10^5\) 遍历到 \(1\) ,设当前遍历到了 \(i\) ,于是记录有多少个 \(i\) 的倍数,然后套上上述的组合问题即可得到 \(g(i)\) 。再减去其倍数的 \(f\) 函数值,即可得到 \(f(i)\) 。扫到 \(1\) 即可得到答案。

AC代码

#include <bits/stdc++.h>
#define rep(i,a,b) for(int i=a;i<b;i++)
using namespace std; 

using ll = long long;

constexpr int N = 2e5 + 5, P = 998244353;
int v[N], f[N], pw[N];

void precompute() {
	pw[0] = 1;
	rep(i,1,N)pw[i]=pw[i-1]*2%P;
}

int main() {
	precompute();
    int n; cin>>n;
	rep(i,0,n){
		int x; cin>>x;
		v[x] ++;
	}
	for(int i=N-1;i;i--){
		int cnt = 0;
		for (int j=i;j<N;j+=i)cnt+=v[j];
		f[i]=(pw[cnt]-1-cnt+P)%P;
		for (int j=i+i;j<N;j+=i)f[i]=(f[i]-f[j]+P)%P;
	}
	cout<<f[1];
	return 0;
}
posted @ 2021-12-19 20:40  xDaniel  阅读(137)  评论(0编辑  收藏  举报