HNCPC2024 2024湖南省赛 题解

写在前面

比赛地址:https://codeforces.com/gym/105423

以下按个人难度向排序。

利益相关:现场赛 Au。

没有和去年一样整场犯唐,好!感谢队友带飞成功一雪前耻。

题解见官方,这里只放一下代码了。

I 签到

#include <bits/stdc++.h>
using namespace std;

#define ll long long
#define ull unsigned long long
const int N=1e5+5;
int read()
{
	int x = 0; bool f = false; char c = getchar();
	while(c < '0' || c > '9') f |= (c == '-'), c = getchar();
	while(c >= '0' && c <= '9') x = (x << 1) + (x << 3) + (c & 15), c = getchar();
	return f ? -x : x;
}
int n,k,m,q;
bool ok[N];
int main() {
	cin>>n>>k>>m>>q;
	for(int i=1,x;i<=m;i++){
		cin>>x;
		int now=x;
		for(int j=1;j<=k;++j){
			now%=n;
			ok[now]=1;	
			now=now*x%n;
		}
	}
	for(int i=1,x;i<=q;i++){
		cin>>x;
		int now=x;
		bool fl=1;
		for(int j=1;j<=k;++j){
			now%=n;
			if(ok[now]==0){
				fl=0;
			}
			now=now*x%n;
		}
		cout<<fl<<' ';
	}
	return 0;
}

C 签到

#include <bits/stdc++.h>
using namespace std;

#define ll long long
#define ull unsigned long long

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

int main() {
	int n; cin >> n;
	
	int cnt = 0;
	
	for (int i = 1; i <= n; ++ i) {
		int a; cin >> a;
		cnt += log2(a);
	}
	
	double k = 1.0 * cnt / log2(2024);
	ll ans = ceil(k);
	
	cout << ans << "\n";
	return 0;
}

E 二进制,枚举,子集 DP

#include <bits/stdc++.h>
using namespace std;

#define ll long long
#define ull unsigned long long

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

const int N = 1e6 + 5;
int n, a[N], vis[N], num[N];

void solve(int pos)
{
	int S = (1 << (a[pos] - 1));
	vis[S] = 1;
	while(pos < n && !(S & (1 << (a[pos + 1] - 1))))
	{
		++pos;
		S |= (1 << (a[pos] - 1));
		vis[S] = 1;
	}
}

int main() {
	n = read();
	for(int i = 1; i <= n; ++i) a[i] = read();
	for(int i = 1; i <= n; ++i) solve(i);
	int ans = 0, flag = (1 << 18) - 1;
	for(int i = 1; i <= flag; ++i) num[i] = num[i >> 1] + (i & 1);
	vis[0] = 1;
	for(int s = 0; s < (1 << 18); ++s)
		for(int t = flag ^ s; t; t = (flag ^ s) & (t - 1))
			if(vis[s] && vis[t]) ans = max(ans, num[s] + num[t]);
	printf("%d", ans);
	return 0;
}

K 转化,分层图最短路

#include <bits/stdc++.h>
using namespace std;

#define ll long long
#define ull unsigned long long

ll read()
{
	ll x = 0; bool f = false; char c = getchar();
	while(c < '0' || c > '9') f |= (c == '-'), c = getchar();
	while(c >= '0' && c <= '9') x = (x << 1) + (x << 3) + (c & 15), c = getchar();
	return f ? -x : x;
}

const int N = 2e5 + 5;
int n, m, S;
vector< pair<int, ll> > V[N];

int vis[N];
ll dis[N];
priority_queue< pair<ll, int> > q;
void dijkstra(int S)
{
	q.push(pair<ll, int>(0, S));
	for(int i = 1; i <= n * 2; ++i) dis[i] = 0x7fffffffffffffff;
	while(!q.empty())
	{
		int now = q.top().second ;
		q.pop();
		if(vis[now]) continue;
		vis[now] = 1;
		for(auto pp : V[now])
		{
			int v = pp.first ;
			ll w = pp.second ;
			if(dis[v] > dis[now] + w)
			{
				dis[v] = dis[now] + w;
				q.push(pair<ll, int>(-dis[v], v));
			}
		}
	}
}

int main() {
	n = read(), m = read();
	S = 2 * n + 1;
	for(int i = 1; i <= m; ++i)
	{
		int u = read(), v = read(), w = read();
		V[u].emplace_back(pair<int, ll>(v, w));
		V[v].emplace_back(pair<int, ll>(u, w));
		V[u].emplace_back(pair<int, ll>(v + n, 0));
		V[v].emplace_back(pair<int, ll>(u + n, 0));
		V[u].emplace_back(pair<int, ll>(u + n, 0));
		V[v].emplace_back(pair<int, ll>(v + n, 0));
		V[u + n].emplace_back(pair<int, ll>(v + n, w));
		V[v + n].emplace_back(pair<int, ll>(u + n, w));
	}
	for(int i = 1; i <= n; ++i)
	{
		ll a = read();
		V[S].emplace_back(pair<int, ll>(i, a));
	}
	dijkstra(S);
	ll ans = 0;
	for(int i = 1; i <= n; ++i) ans = max(ans, dis[i + n]);
	printf("%lld\n", ans);
	return 0;
}

A 枚举,DP,简单计算几何

#include <bits/stdc++.h>
using namespace std;

#define LL long long
#define ull unsigned long long
#define int long long
const int kN = 1e3 + 10;
const double eps=1e-9;
int n,x[kN],y[kN], into[kN];
int ans=0;
struct node{
	int x,y;
};
node get_node(int a,int b){
	return (node){x[b]-x[a],y[b]-y[a]};
}
int f[55];
double len(node a){
	return sqrt(a.x*a.x+a.y*a.y);
}
bool check(node a,node b,bool fl){
	if(fl){
		if(a.x*b.y-a.y*b.x<0) return 0;
	}else{
		if(a.x*b.y-a.y*b.x>0) return 0;
	}
	double oo=(1.0*a.x*b.x+1.0*a.y*b.y)/(1.00*len(a)*len(b));
	if(oo<0-eps) return 0;
	if(oo-eps>1) return 0;
	return 1;
}
void get(node now,bool fl){
	for(int i=1;i<=n;++i) f[i]=-9999999;
	vector<int> edge[kN];
	for (int i = 0; i <= n; ++ i) into[i] = 0;
	
	for(int i=0;i<=n;++i){
		if (!check(get_node(0, i), now, fl)) continue;
		for(int j=1;j<=n;++j){
			if(i==j) continue;
			if (!check(get_node(0, j), now, fl)) continue;
			if(check(get_node(i,j),now,fl)){
				edge[i].push_back(j);
				++ into[j];
			}
		}
	}
	
	queue<int> q;
	q.push(0);
	while (!q.empty()) {
		int u = q.front(); q.pop();
		for (auto v: edge[u]) {
			f[v] = max(f[v], f[u] + 1);
			if (!(--into[v])) q.push(v);
		}
	}
	
	for(int i=0;i<=n;++i) ans=max(ans,f[i]);
}
signed main(){
	cin>>n;
	f[0]=0;
	x[0]=0,y[0]=0;
	for(int i=1;i<=n;++i){
		cin>>x[i]>>y[i];
		if(x[i]==0&&y[i]==0){
			f[0]++;
			--i;
			--n;
		}
		
	}
	ans=f[0];
	for(int i=0;i<=n;++i){
		for(int j=0;j<=n;++j){
			if(i==j) continue;
			get(get_node(i,j),0);
			get(get_node(i,j),1);
		}
	}
	cout<<ans<<endl;
	return 0;
}

J 单调性,枚举,数据结构

#include <bits/stdc++.h>
using namespace std;
#define int long long
#define ll long long
#define ull unsigned long long

const int N = 1e6 + 10;

int read()
{
	int x = 0; bool f = false; char c = getchar();
	while(c < '0' || c > '9') f |= (c == '-'), c = getchar();
	while(c >= '0' && c <= '9') x = (x << 1) + (x << 3) + (c & 15), c = getchar();
	return f ? -x : x;
}
int n;
int a[N],b[N],ans=0;
int ida[N],idb[N];
set<int> s;

struct BIT {
	#define low(x) ((x)&(-x))
	int sum[N];
	int lim;
	void init(int lim_) {
		lim = lim_;
		for (int i = 1; i <= lim; ++ i) sum[i] = 0;
	}
	void insert(int p_, int val_) {
		for (int i = p_; i <= lim; i += low(i)) {
			sum[i] += val_;
		}
	}
	int Sum(int p_) {
		int ret = 0;
		for (int i = p_; i; i -= low(i)) ret += sum[i];
		return ret;
	}
} bit[2];

bool check(int x){
	int rka = bit[0].Sum(ida[x]);
	int rkb = bit[1].Sum(idb[x]);
	return rka == rkb;
}
void push(int x){
	bit[0].insert(ida[x], 1);
	bit[1].insert(idb[x], 1);
}
void del(int x){
	bit[0].insert(ida[x], -1);
	bit[1].insert(idb[x], -1);
}
signed main() {
	cin>>n;
	bit[0].init(n);
	bit[1].init(n);
	for(int i=1;i<=n;++i){
		cin>>a[i];
		ida[a[i]]=i;
	}
	for(int i=1;i<=n;++i){
		cin>>b[i];
		idb[b[i]]=i;
	}
	int r=1;
	push(r);
	for(int l=1;l<=n;++l){
		while(r+1<=n&&check(r+1)){
			push(r+1);
			++r;
		}
		ans+=(r-l+1);
		del(l);
	}
	cout<<ans;
	return 0;
}

H DP,字符串,KMP

#include <bits/stdc++.h>
using namespace std;

#define LL long long
#define ull unsigned long long

const int kN = 10010;
const LL p = 998244353;

int n, k;

LL f[kN][13][110];
int fail[110];
int len;

char s[110]; 

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

int main() {
	n = read(), k = read();
	scanf("%s", s + 1);
	len = strlen(s + 1);
	
	fail[1] = 0;
	for (int i = 2, j = 0; i <= len; ++ i) {
		while (j && s[i] != s[j + 1]) j = fail[j];
		if (s[i] == s[j + 1]) ++ j;
		fail[i] = j;
	}
	
	f[0][0][0] = 1;
	for (int i = 0; i < n; ++ i) {
		for (int j = 0; j <= k; ++ j) {
			for (int l = 0; l < len; ++ l) {
				for (int c = 0; c < 26; ++ c) {
					bool flag = 0;
					for (int pre = l; 1; pre = fail[pre]) {
						if (s[pre + 1] == (char)(c + 'a')) {
							if (pre == len - 1) {
								f[i + 1][j + 1][0] += f[i][j][l];
								f[i + 1][j + 1][0] %= p;
							} else {
								f[i + 1][j][pre + 1] += f[i][j][l];
								f[i + 1][j][pre + 1] %= p;
							}
							flag = 1;
							break;
						}
						if (pre == 0) break;
					}
					if (flag) continue;
					f[i + 1][j][0] += f[i][j][l];
					f[i + 1][j][0] %= p;
				}
			}
		}
	}
	
//	for (int i = 1; i <= n; ++ i) {
//		for (int j = 0; j <= k; ++ j) {
//			for (int l = 0; l <= len; ++ l) {
////				printf("%d %d %d %lld\n", i, j, l, f[i][j][l]);
//			}
//		}
//	}
	
	LL ans = 0;
	for (int i = 0; i < len; ++ i) {
		ans += f[n][k][i];
		ans %= p;
	}
	printf("%lld\n", ans);
	return 0;
}
/*
4 1
aa
*/

D 莫比乌斯反演,枚举

前置知识:莫比乌斯反演

艾佛森括号\([P] = \begin{cases} 1 &\text{If P is true}\\ 0 &\text{Otherwise} \end{cases}\)
此处 \(P\) 是一个可真可假的命题。

发现给定的式子需要枚举 \(b_i, b_j\) 并求在分母上的 \(\gcd\),非常丑陋,于是套路地考虑枚举 \(\gcd\),并考虑有多少对 \(i, j\) 可以对枚举的 \(\gcd\) 产生贡献,想到对 \(\gcd\) 进行莫比乌斯反演。

考虑如下经典的反演套路:

\[\left[\gcd(i,j) = 1\right] = \sum\limits_{d\mid \gcd(i,j)} {\mu (d)} = \sum_{d | i,j} \mu(d) \]

于是考虑枚举 \(d| b_i,b_j\),原式转化为:

\[\begin{aligned} &\sum_{i=1}^{n}\sum_{j = 1}^n \sum_{d| b_i,b_j}|a_i - a_j| \dfrac{b_ib_j}{d^2}[\gcd(b_i, b_j) = d] \\ = &\sum_{i=1}^{n}\sum_{j = 1}^n \sum_{d| b_i,b_j}\left|a_i - a_j\right| \dfrac{b_i}{d}\dfrac{b_j}{d} \left[\gcd\left(\frac{b_i}{d}, \frac{b_j}{d}\right) = 1\right]\\ = &\sum_{i=1}^n\sum_{j=1}^n\sum_{d| b_i,b_j}\left|a_i - a_j\right|\sum_{k|\gcd\left(\frac{b_i}{d}, \frac{b_j}{d}\right)} \mu(k)\dfrac{b_i}{d}\dfrac{b_j}{d}\\ \end{aligned}\]

然后发现推不下去了。但是发现 \(k\)\(d\) 实际上是存在对 \(b_i,b_j\) 的约数的关系的,即有 \(kd | b_i,b_j\)。于是套路地考虑转化枚举对象(这个套路在P3768 简单的数学题 里见过),考虑将枚举 \(d, k\) 转化为枚举 \(T=kd\)\(k|T\),则有 \(d = \frac{T}{k}\),则上式可转化为:

\[\begin{aligned} &\sum_{i=1}^n\sum_{j=1}^n\sum_{d| b_i,b_j}\left|a_i - a_j\right|\sum_{k|\gcd\left(\frac{b_i}{d}, \frac{b_j}{d}\right)} \mu(k)\dfrac{b_i}{d}\dfrac{b_j}{d}\\ =&\sum_{i=1}^n\sum_{j=1}^n\sum_{T| b_i,b_j}\left|a_i - a_j\right|\sum_{k|T} \mu(k)k^2\dfrac{b_i}{T}\dfrac{b_j}{T}\\ =&\sum_{i=1}^n\sum_{j=1}^n\sum_{T| b_i,b_j}\left|\frac{(a_i - a_j)b_ib_j}{T^2}\right|\sum_{k|T} \mu(k)k^2 \end{aligned}\]

于是得到了题解的式子。

用到了同样套路的例题推荐:

#include<bits/stdc++.h>
using namespace std;

#define int long long
#define ll long long
#define ull unsigned long long

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

const ll mod = 998244353;
const int N = 2e5;

int n;
struct node{ ll a, b; } A[N + 5];

bool cmp(const node &a, const node &b)
{ return (a.a == b.a ) ? (a.b < b.b ) : (a.a < b.a ); }

int vis[N + 5], prime[N + 5], cnt;
ll jc[N + 5], inv[N + 5], u[N + 5], f[N + 5];
vector<int> yve[N + 5];

ll qpow(ll a, ll b, ll mod)
{
	ll ans = 1;
	while(b)
	{
		if(b & 1) ans = ans * a % mod;
		b >>= 1;
		a = a * a % mod;
	}
	return ans;
}

void init()
{
	u[1] = 1;
	for(int i = 2; i <= N; ++i)
	{
		if(!vis[i]) prime[++cnt] = i, u[i] = -1;
		for(int j = 1; j <= cnt && prime[j] * i <= N; ++j)
		{
			vis[prime[j] * i] = 1;
			if(i % prime[j] == 0) break;
			u[i * prime[j]] = -u[i];
		}
	}

	for(int i = 1; i <= N; ++i)
		for(int j = 1; j * i <= N; ++j)
		{
			f[i * j] = (f[i * j] + u[i] * i * i % mod + mod) % mod;
			yve[i * j].emplace_back(i);
		}
	
	// 求单个数的逆元
	jc[0] = jc[1] = inv[0] = inv[1] = 1;
	for(int i = 2; i <= N; ++i) jc[i] = jc[i - 1] * i % mod;
	inv[N] = qpow(jc[N], mod - 2, mod);
	for(int i = N - 1; i >= 2; --i) inv[i] = inv[i + 1] * (i + 1) % mod;
	for(int i = 2; i <= N; ++i) inv[i] = inv[i] * jc[i - 1] % mod;

	for(int i = 2; i <= N; ++i) f[i] = f[i] * inv[i] % mod * inv[i] % mod;
}

ll Sum1[N + 5], Sum2[N + 5], ans;

signed main()
{
	init();
	n = read();
	for(int i = 1; i <= n; ++i) A[i].a = read();
	for(int i = 1; i <= n; ++i) A[i].b = read();
	sort(A + 1, A + n + 1, cmp);
	for(int i = 1; i <= n; ++i)
		for(auto T : yve[A[i].b ])
		{
			ans = (ans + f[T] * ((A[i].a * A[i].b % mod * Sum2[T] % mod - A[i].b * Sum1[T] % mod + mod) % mod) % mod) % mod;
			Sum1[T] = (Sum1[T] + A[i].a * A[i].b ) % mod;
			Sum2[T] = (Sum2[T] + A[i].b ) % mod;
		}
	ans = (ans % mod + mod) % mod;
	printf("%lld\n", ans * 2 % mod);
    return 0;
}

写在最后

回来之后厌学了。

posted @ 2024-10-16 21:23  Luckyblock  阅读(1117)  评论(7编辑  收藏  举报