首页 > 其他分享 >2023ccpc湖北省赛/2023 Hubei Provincial Collegiate Programming Contest个人题解

2023ccpc湖北省赛/2023 Hubei Provincial Collegiate Programming Contest个人题解

时间:2023-05-07 20:34:49浏览次数:45  
标签:prime Provincial Contest int 题解 eg flow first dis

2023 Hubei Provincial Collegiate Programming Contest

A Prime Magic

Walk Alone has a sequence \(a_1,a_2,...,a_n\), and he can use a magic on it:

  • Choose an odd prime number \(p\) and an interval \([l,r]⊆[1,n]\) satisfying \(r−l+1=p\), and then add \(1\) or \(−1\) to every \(a_i\) in this interval. That is, choose \(x∈{1,−1}\), then \(∀i∈[l,r],ai←ai+x\).

Remind that odd prime is numbers that are odd greater than \(1\) and can only be divided by \(1\) and itself.

Walk Alone likes growing things, so he wants to use this magic to make this sequence non-decreasing. Besides, Walk Alone doesn't like negative numbers, so during the whole operations any number cannot be negative.

He wants to accomplish this idea with minimal number of magic usage. Although Walk Alone has this magic, he doesn't know in which order to use it, so he asks you for help.

For each test case, output a single integer in a line denoting the minimum number of magic usage to accomplish the task.

\(1\le T\le100,10\le n\le2e3,1\le a_i\le1e5,\sum n\le2e4\)

中文题意(参考官方题解) 给定一长度为 $n$ 的序列 ${a_n}$,每次可以选取一段长度为奇质数的连续子序列 $[l, r]$ 整体加一或减一,要求操作过程中序列中不得出现负数,问将整个序列转化为非严格递增序列的最少操作次数。
Solution 操作可以转化为在差分数组上使一个点加一,同时另一个点减一,这两个点的距离是奇质数

这样就转化为了费用流问题,每个正的 \(diff_i\) 可以为一个负的 \(diff_j\) 提供 \(diff_i\) 次操作,最终使差分数组非负

朴素想法是按照 \(diff\) 的正负分类建二分图,连 \(n^2\) 条边。

  • 对于距离为奇质数的点对,单位流量费用为 \(1\)
  • 对于距离为偶合数的点对,根据哥德巴赫猜想,一定可以表示为两个素数之和,单位流量费用为 \(2\);特别地,距离为 \(2\) 的点对费用也是 \(2\)
  • 对于距离为奇合数的点对,一定可以表示为 \(3+prime_a+prime_b\) 的形式(\(n\ge10\)是为了保证这个有解),单位流量费用为 3

然而边太多了,而且费用流比较慢无法通过此题

观察到费用只有 1,2,3;而且主要是 2 和 3,想办法转化为最大流问题

官方题解提供了一种优化方法:

  1. 首先对费用为 \(1\) 的边进行匹配
  2. 费用为 \(2\) 的边:二分图左右部点在 \(1\) 操作后的残量图中直接两两尝试匹配
  3. 剩下无法匹配的边只能按照费用为 \(3\) 的费用匹配

贪心的正确性来自于费用为 \(1, 3\) 的边只会对奇偶性不同的点连接,而费用为 \(2\) 的边只会对奇偶性相同的点连接;

二者是独立的,而奇偶性不同(代价可 \(1\) 可 \(3\))的情况下当然优先选择 \(1\) qaq

最大流dinic算法在二分图中复杂度为 \(O(n\sqrt m)\),\(m\approx \frac{n^2}{logn}\),故一组测试复杂度 \(O(\frac{n^{2.5}}{logn})\),取上界的时候最多 10 组,可以通过

Code
const int maxn = 2e4 + 5, maxm = 6e6 + 6;
const int INF = 0x3f3f3f3f;
//const ll INF = 0x3f3f3f3f3f3f3f3f;
struct MaxFlow {
	struct edge {
		int u, v, cap, flow;
		edge() {}
		edge(int u, int v, int cap, int flow) :u(u), v(v), cap(cap), flow(flow) {}
	}eg[maxm << 1];
	int n;
	int tot, dis[maxn << 1], cur[maxn << 1];
	vector<int> tab[maxn << 1];
	void init(int _n) {
		tot = 0;	n = _n;
		for (int i = 0; i <= n; ++i)	tab[i].clear();
	}
	void addedge(int u, int v, int cap) {
		tab[u].push_back(tot);
		eg[tot++] = edge(u, v, cap, 0);
		tab[v].push_back(tot);
		eg[tot++] = edge(v, u, 0, 0);
	}
	int bfs(int s, int t) {
		queue<int> q;
		q.push(s);
		//memset(dis, INF, sizeof dis);
		for (int i = 0; i <= n; ++i)	dis[i] = INF;
		dis[s] = 0;
		while (!q.empty()) {
			int h = q.front(), i;
			q.pop();
			for (i = 0; i < tab[h].size(); ++i) {
				edge& e = eg[tab[h][i]];
				if (e.cap > e.flow && dis[e.v] == INF) {
					dis[e.v] = dis[h] + 1;
					q.push(e.v);
				}
			}
		}
		return dis[t] < INF;
	}
	int dfs(int x, int maxflow, int s, int t) {
		if (x == t || maxflow == 0) return maxflow;
		int flow = 0, i, f;
		for (i = cur[x]; i < tab[x].size(); ++i) {
			cur[x] = i;
			edge& e = eg[tab[x][i]];
			if (dis[e.v] == dis[x] + 1 && (f = dfs(e.v, min(maxflow, e.cap - e.flow), s, t)) > 0) {
				e.flow += f;
				eg[tab[x][i] ^ 1].flow -= f;
				flow += f;
				maxflow -= f;
				if (maxflow == 0)   break;
			}
		}
		return flow;
	}
	int dinic(int s, int t) {
		int flow = 0;
		while (bfs(s, t)) {
			//memset(cur, 0, sizeof(cur));
			for (int i = 0; i <= n; ++i)	cur[i] = 0;
			flow += dfs(s, INF, s, t);
		}
		return flow;
	}
}F;
const int M = 2e4 + 5;
bitset<M + 5> is_prime;
int prime[M + 5];
void get_prime() {
	is_prime.set();	is_prime[0] = is_prime[1] = 0;
	int cnt = 0;
	for (int i = 2; i <= M; ++i) {
		if (is_prime[i]) prime[++cnt] = i;
		for (int j = 1; j <= cnt; ++j) {
			if (i * prime[j] > M)    break;
			is_prime[i * prime[j]] = 0;
			if (i % prime[j] == 0)   break;
		}
	}
	prime[0] = cnt;
}
void AC() {
	int n;	cin >> n;
	vector<int> A(n + 1);
	for (int i = 1; i <= n; ++i)	cin >> A[i];
	for (int i = n; i >= 1; --i)	A[i] = A[i] - A[i - 1];
	A[n + 1] = INF;
	vector<pii> pos, neg;
	for (int i = 1; i <= n + 1; ++i) {
		if (A[i] > 0) pos.push_back(make_pair(A[i], i));
		if (A[i] < 0) neg.push_back(make_pair(-A[i], i));
	}
	int s = n + 2, t = n + 3;
	F.init(n + 3);
	for (auto& x : pos) {//(n/2)^2/logn 条边
		for (auto& y : neg) {
			int dis = abs(x.second - y.second);
			if (dis != 2 && is_prime[dis]) {
				F.addedge(x.second, y.second, x.first);
			}
		}
	}
	for (auto& x : pos) F.addedge(s, x.second, x.first);
	for (auto& x : neg) F.addedge(x.second, t, x.first);
	int ans = F.dinic(s, t);
	pos.clear();	neg.clear();
	for (int i = 0; i < F.tot; i += 2) {
		int rest = F.eg[i].cap - F.eg[i].flow;
		if (F.eg[i].u == s && rest) pos.push_back(make_pair(rest, F.eg[i].v));
		if (F.eg[i].v == t && rest)	neg.push_back(make_pair(rest, F.eg[i].u));
	}
	for (auto& y : neg) {
		for (auto& x : pos) {
			if ((x.second & 1) == (y.second & 1)) {
				int dec = min(y.first, x.first);
				y.first -= dec;
				ans += dec * 2;
				if (!y.first)	break;
			}
		}
	}
	for (auto& y : neg) {
		if (!y.first)	continue;
		ans += y.first * 3;
	}
	cout << ans << endl;
}
signed main() {
	ios;    int o_o = 1;
	get_prime();
	cin >> o_o;
	while (o_o--)  AC();
	return 0;
}

标签:prime,Provincial,Contest,int,题解,eg,flow,first,dis
From: https://www.cnblogs.com/JU5T4FUN/p/17380076.html

相关文章

  • MultiDex使用方法及由此导致的crash、ANR问题解决方案
    Android开发的朋友,如果是在开发一款中大型应用时,都会碰到这么一个问题,就是dex分拆问题,google给出的解决方案MultiDex。现象:有些APP本身功能比较多,再加上一些其它三方的SDK,慢慢的发现dex越来越大,直到有一天编译出现如下错误:Error:Thenumberofmethodreferencesina.dexfile......
  • 十二代酷睿处理器N100 N200 N305 等安装ESXI紫屏问题解决办法
    12代大小核紫屏报错解决方案四部曲1、安装界面倒计时结束之前,按SHIFT+O,在原有命令后面加空格后输入以下代码cpuUniformityHardCheckPanic=FALSE2、安装完ESXI之后会重启,在重启界面倒计时结束前,再操作一次,按住SHIFT+O,输入cpuUniformityHardCheckPanic=FALSE3、进入ESXI把SSH功能......
  • 力扣《环形链表Ⅱ》超详细题解
    作为经典题目的《环形链表Ⅱ》,我认为这题还是有专门出一期博客的分量的,大家也可以自己事先按照自己的理解写一写,然后再来看题解,毕竟自己悟出来的东西是比吸收别人的来的更深刻。一、题目给定一个链表的头节点 head ,返回链表开始入环的第一个节点。 如果链表无环,则返回 null。......
  • CF1829B 题解
    题目思路先定义变量\(t,ans\)。循环从\(0\)到\(n-1\),对于第\(i\)个数,如果为\(0\),\(t=t+1\),否则将\(t\)清零。每次循环\(ans=\max(ans,t)\)表示最多有多少个连续的\(0\)。最后输出\(ans\)即可。核心代码点击查看代码voidsolve(){intn=read(),a[1005],a......
  • AtCoder Regular Contest 159简要题解
    AtCoderRegularContest159传送门A-CopyandPasteGraph图的邻接矩阵为\[\left(\begin{matrix}A&A&\cdots&A\\A&A&\cdots&A\\\cdots&\cdots&\cdots&\cdots\\A&A&\cdots&A\e......
  • AtCoder Regular Contest 134 E Modulo Nim
    洛谷传送门AtCoder传送门It'sallMAGIC这种题目一般先考虑局面要满足什么条件能必胜,然后根据这个性质来计数。如果把黑板上的数写成一个集合\(S\),那么:\(\varnothing\)为必胜态,\(\{1\},\{2\}\)显然为必败态,打表发现其他单元素集合都为必胜态;如果\(\existsx\inS,x......
  • P8655 [蓝桥杯 2017 国 B] 发现环 题解
    题目概述题目传送门在一棵树中新增一条边,使得这个图产生一个环,求在环上的点。思路:拓补排序对于这道题显然不能生搬硬套拓补排序的模板。这道题中的图是一个无向图,而拓补排序却是处理有向图的一种思想。不难想到可以将无向图转化为有向图,即将对于每条无向边变换为双向建边,就......
  • AtCoder Beginner Contest 242
    A-T-shirt#include<bits/stdc++.h>usingnamespacestd;int32_tmain(){doublea,b,c,x; cin>>a>>b>>c>>x; if(x<=a)cout<<"1.000000000000"; elseif(x>b)cout<<&qu......
  • AtCoder Beginner Contest 285(B,D,E,F)
    AtCoderBeginnerContest285(B,D,E,F)B(暴力,非二分)B这道题其实很简单,但是我在\(vp\)的过程,有了一个错误的认识,纠正一下那就是,我把这个当成了一个二分题,并且还有点坚定不移,后来细想,发现不对二分,适用于那种边界分明的那种题(左边一定是符合条件,右边一定符合条件,也可表达为线性......
  • ABC297F 题解
    容斥萌萌题给你一个\(H\timesW\)的棋盘,问在棋盘上随机撒\(k\)个点,能够围住这\(k\)个点的最小子矩形的期望面积考虑枚举子矩形可以直接转成计数问题转变为在\(n\timesm\)的矩形中撒\(k\)个点,有多少种方案使得四条边上均至少有一个点答案乘上矩形面积再除以所有撒点......