【做题】arc080_f-Prime Flip——转换、数论及匹配
题意:有一个无限序列,其中有\(n\)个位置上的数为\(1\),其余都是\(0\)。你可以进行若干次操作,每次选取序列上的一个区间\([l,r)\),满足\(r-l\)为奇质数,将在这个区间上的数都异或上\(1\)。问最少进行多少次操作,使得序列上所有数都变为\(0\)。
\(n \leq 100\)
这类自己决定操作来让数列变为全零的问题,在atcoder上是挺常见的。
一般对于这种问题,要用差分或类似的操作,来让区间修改变为单点修改。例如,对于区间加的操作,就用差分将其变为两个单点加。至于这个问题,我们设原序列为数列\(\{a\}\),则构建数列\(\{b\}\)满足\(b_n=a_{n-1} \,{\rm xor} \,a_n\)。这样,题目中的\([l,r)\)区间异或\(1\)就变成了将\(b_l\)和\(b_r\)都异或上\(1\)。
接下来,我们考虑操作可以被分为几个部分,每一个部分就是把两个为\(1\)的元素变为\(0\),其余元素均不改变状态。于是,问题就变成了对\(\{b\}\)中为\(1\)的元素进行匹配。(可以发现,为\(1\)的元素的总数为偶数)
我们考虑两个位置的距离为\(d\)的元素,将他们都变为\(0\),需要多少次操作。这等价于把\(d\)表示为最少数量的质数的和或差。
- \(d\)为奇质数。那么,显然一次操作就可以了。
- \(d\)为偶数。首先,从奇偶性上看,只用一次操作是不可能的。故至少需要两次操作。考虑\(d=2\)时,可以由\(2=5-3\)两次操作完成(存在位置离其中一个元素为\(5\),另一个为\(3\))。而若\(d \geq 4\),由于哥德巴赫猜想在本题数据范围内是成立的,故一定能表示为两个质数的和,即可以用两次操作实现。
- \(d\)为奇合数。显然一次操作或两次操作都是不可能的。而\(d\)加上一个奇质数后是一个偶数,故也最多需要三次操作。
剩下的就是匹配的问题了。这里不能拆点做二分图带权最大匹配,很容易能举出反例。但是,我们可以用贪心来把所有元素分为两组。(此处瞄了题解)设\(k\)为所有匹配中\(d\)为奇质数的组数。那么,显然是\(k\)组奇数位置和偶数位置匹配。设奇数的位置上有\(n_1\)个元素,偶数的位置上有\(n_2\)个元素。那么,显然要让奇偶性相同的元素匹配到一起,操作总数最小。于是,我们可以得出,总操作数为$$k+2 \times (\lfloor \frac {n_0-k} {2}\rfloor+\lfloor \frac {n_1-k} {2} \rfloor) + 3 \times ((n_0 - k) \mod 2)$$
由于元素总数为偶数,\(n_0\)和\(n_1\)同奇偶,我们分奇偶讨论一下就能得到对于所有合法的\(k\)和\(k+1\),\(f(k+1) \leq f(k)\)。因此,我们只要最大化\(k\)就可以了。我们根据奇偶性对元素分组,然后做二分图最大匹配就可以了。
时间复杂度\(O(n^2 + m)\)。\(m\)为权值大小。
#include <bits/stdc++.h>
using namespace std;
const int N = 610, INF = 0x3f3f3f3f;
int p[N],n,cnt,st,en,x[N];
struct edge {
int la,b,cap;
} con[N * N * 2];
int tot=1,fir[N];
void add(int from,int to,int capc) {
con[++tot] = (edge) {fir[from],to,capc};
fir[from] = tot;
con[++tot] = (edge) {fir[to],from,0};
fir[to] = tot;
}
int cur[N], dis[N];
int dfs(int pos,int imp) {
if (pos == en || (!imp)) return imp;
int expo = 0, tmp;
for (int &i = cur[pos] ; i ; i = con[i].la) {
if (dis[con[i].b] == dis[pos] + 1) {
tmp = dfs(con[i].b,min(imp,con[i].cap));
con[i].cap -= tmp;
con[i^1].cap += tmp;
expo += tmp;
imp -= tmp;
if (!imp) break;
}
}
return expo;
}
bool bfs() {
static queue<int> q;
while (!q.empty()) q.pop();
memset(dis,0,sizeof dis);
for (int i = 1 ; i <= n ; ++ i)
cur[i] = fir[i];
dis[st] = 1;
q.push(st);
for (int pos ; !q.empty() ; q.pop()) {
pos = q.front();
for (int i = fir[pos] ; i ; i = con[i].la) {
if (con[i].cap && (!dis[con[i].b])) {
dis[con[i].b] = dis[pos] + 1;
q.push(con[i].b);
}
}
}
if (!dis[en]) return 0;
return 1;
}
const int MAX = 10000010;
int isp[MAX + 10], pri[MAX / 10], pcnt, num[2];
set<int> prime;
void prework() {
for (int i = 2 ; i <= MAX ; ++ i) {
if (!isp[i]) pri[++pcnt] = i;
for (int j = 1 ; j <= pcnt && pri[j] * i <= MAX ; ++ j) {
isp[pri[j] * i] = 1;
if (i % pri[j] == 0) break;
}
}
for (int i = 2 ; i <= pcnt ; ++ i)
prime.insert(pri[i]);
}
int main() {
prework();
scanf("%d",&n);
for (int i = 1 ; i <= n ; ++ i) {
scanf("%d",&x[i]);
if (x[i] == 1 || x[i] != x[i-1] + 1)
p[++cnt] = x[i];
if (i > 1 && x[i] != x[i-1] + 1)
p[++cnt] = x[i-1] + 1;
}
p[++cnt] = x[n] + 1;
n = cnt;
st = ++n;
en = ++n;
for (int i = 1 ; i <= cnt ; ++ i) {
++ num[p[i]&1];
if (p[i]&1) {
add(st,i,1);
for (int j = 1 ; j <= cnt ; ++ j) {
int d = abs(p[i] - p[j]);
if (prime.count(d))
add(i,j,1);
}
} else add(i,en,1);
}
int ans = 0;
while (bfs())
ans += dfs(st,INF);
printf("%d\n",ans + 2 * ((num[0] - ans) / 2 + (num[1] - ans) / 2) + ((num[1] - ans)&1) * 3);
return 0;
}
小结:又看了一道思维题的题解……感觉表算高妙的同时,自己还深有不足。
【做题】arc080_f-Prime Flip——转换、数论及匹配的更多相关文章
- Codeforces & Atcoder神仙题做题记录
鉴于Codeforces和atcoder上有很多神题,即使发呆了一整节数学课也是肝不出来,所以就记录一下. AGC033B LRUD Game 只要横坐标或者纵坐标超出范围就可以,所以我们只用看其中一 ...
- C语言程序设计做题笔记之C语言基础知识(下)
C 语言是一种功能强大.简洁的计算机语言,通过它可以编写程序,指挥计算机完成指定的任务.我们可以利用C语言创建程序(即一组指令),并让计算机依指令行 事.并且C是相当灵活的,用于执行计算机程序能完成的 ...
- C语言程序设计做题笔记之C语言基础知识(上)
C语言是一种功能强大.简洁的计算机语言,通过它可以编写程序,指挥计算机完成指定的任务.我们可以利用C语言创建程序(即一组指令),并让计算机依指令行事.并且C是相当灵活的,用于执行计算机程序能完成的几乎 ...
- ACM 做题过程中的一些小技巧。
ACM做题过程中的一些小技巧. 1.一般用C语言节约空间,要用C++库函数或STL时才用C++; cout.cin和printf.scanf最好不要混用. 2.有时候int型不够用,可以用long l ...
- [日记&做题记录]-Noip2016提高组复赛 倒数十天
写这篇博客的时候有点激动 为了让自己不颓 还是写写日记 存存模板 Nov.8 2016 今天早上买了两个蛋挞 吃了一个 然后就做数论(前天晚上还是想放弃数论 但是昨天被数论虐了 woc noip模拟赛 ...
- AtCoder Grand Contest 11~17 做题小记
原文链接https://www.cnblogs.com/zhouzhendong/p/AtCoder-Grand-Contest-from-11-to-20.html UPD(2018-11-16): ...
- AtCoder Grand Contest 1~10 做题小记
原文链接https://www.cnblogs.com/zhouzhendong/p/AtCoder-Grand-Contest-from-1-to-10.html 考虑到博客内容较多,编辑不方便的情 ...
- noip做题记录+挑战一句话题解?
因为灵巧实在太弱辽不得不做点noip续下命QQAQQQ 2018 积木大赛/铺设道路 傻逼原题? 然后傻逼的我居然检查了半天是不是有陷阱最后花了差不多一个小时才做掉我做过的原题...真的傻逼了我:( ...
- PKUWC/SC 做题笔记
去年不知道干了些啥,什么省选/营题都没做. 现在赶应该还来得及(?) 「PKUWC2018」Minimax Done 2019.12.04 9:38:55 线段树合并船新玩法??? \(O(n^2)\ ...
随机推荐
- CG标准函数库
(1)数学函数 函数 功能描述 abs(x) 返回输入参数的绝对值 acos(x) 反余切函数,输入参数范围为[-1,1], 返回[0,π]区间的角度值 all(x) 如果输入参数均不为0,则返回tu ...
- RAMPS1.4 3D打印控制板:软件下载\连接\安装\测试
RAMPS1.4 3D打印控制板:软件下载\连接\安装\测试 特别说明: 电源接反,电机驱动板接反将有可能烧毁芯片和电路,请再三确认后再进行通电. 如何使用: 1.需要用到的模块或器件: Arduin ...
- jQuery事件--blur()和focus()
blur([[data],fn]) 概述 当元素失去焦点时触发 blur 事件. 这个函数会调用执行绑定到blur事件的所有函数,包括浏览器的默认行为.可以通过返回false来防止触发浏览器的默 ...
- PYQT5学习笔记之各模块介绍
Qtwidgets模块包含创造经典桌面风格的用户界面提供了一套UI元素的类 Qtwidegts下还有以下常用对象,所以一般使用Qtwidegts时会使用面向对象式编程 QApplication: ap ...
- tensorflow学习5----GAN模型初探
生成模型: 通过观测学习样本和标签的联合概率分布P(X,Y)进行训练,训练好的模型能够生成符合样本分布的新数据,在无监督学习方面,生成式模型能够捕获数据的高阶相关性,通过学习真实数据的本质特征,刻画样 ...
- 20165316 技能学习心得与c语言学习
20165316 技能学习心得与c语言学习 一.技能学习经验 我会打乒乓球,在中国,我只能说我"会"打,至于"比大多数人更好"我不敢断言,因为我无时无刻不感受到 ...
- 转:C#清除回收站
SHEmptyRecycleBin是一个内核API方法,该方法能够清空回收站中的文件,该方法在C#中需要手动的引入方法所在的类库.该方法在C#中的声明语法如下: [DllImportAttrbute( ...
- Linux下java nohup 后台运行关闭后进程停止的原因,不挂断后台运行命令
Linux下java nohup 后台运行关闭后进程停止的原因,不挂断后台运行命令 今天写sh脚本发现一终止命令程序就停止运行了,检查了很久才发现后面少了个&字符导致的!错误写法:nohup ...
- python 关键字yield解析
python 关键字yield解析 yield 的作用就是把一个函数变成一个 generator,带有 yield 的函数不再是一个普通函数,Python 解释器会将其视为一个 generator.y ...
- [C#基础]说说lock到底锁谁?(补充与修改)
摘要 今天在园子里面有园友反馈关于[C#基础]说说lock到底锁谁?文章中lock(this)的问题.后来针对文章中的例子,仔细想了一下,确实不准确,才有了这篇文章的补充,已经对文章中的demo进行修 ...