NOIP 2005 Senior 3
来源:互联网 发布:祛痘消炎的药膏知乎 编辑:程序博客网 时间:2024/05/19 00:17
传送门(OpenJudge)
过了很久重拾此题,发现正好用到了昨天做的题 ( (93) sequence(JZOJ) ) 中的差分判断思想。又发现好像我过去连题意都理解错了。。。
首先是构造一个合法序列。很容易得知,如果一个人要和自己相邻,或者有多于两个人要与某个人相邻,那么一定是不合法的。然后我们就可以直接用循环代替DFS在“图”中找一个环。如果找到的环的大小不为n,说明一定不能满足,否则一定有解。(用循环找,有没有可能找到了一条σ形状的环呢?不可能!如果有,那么闭合处的点的度为3,已经被排除了)
于是我们得到了一个序列,代表最终的合法序列。当然,根据题意,这个序列应该是一个环,也就是说如果我们要用链来表示,它将有n个等价的表示。另外根据题意,这个环还可以反着来,也就是说总共有2n个线型序列来表示这个合法的环。
然后就是根据题意来换了。观察题意后发现,这不就是某某年又某某年初赛的某某题吗,首先如果位置对了的人就不用换了,剩下要换的交换顺序一定构成一个圈(如 1换2,2换4,4换1),这正好和题目中的操作一样。又因为操作的代价按人头算,因此题目实际上是要求 (n - 位置对了的人的数量的最大值)。
朴素算法就是把所有合法序列拿来和(1,2,3,…,n)一一对比,时间复杂度O(n^2)。有没有更快的方法呢?
我们换个旋转方法。假设合法的序列(设为A[1 ~ n])不动,原序列(设为B[1 ~ n])做形如(1,2,3,…,n)到(2,3,4,…,n,1)的变化。
我们在A中假设只留下位置已经正确了的。虽然不知道是哪些,但是我们知道,如果这些正确的为
然后你就知道了:
对于这道题,等价于:
也就是说,它们可能值不一样,但是如果差一样,就能通过旋转B来得到一样的。
所以统计差值。出现次数最大的差值的次数即为位置对了的人的数目的最大值。
参考代码
#include <cstdio>#include <cstdlib>#include <cmath>#include <cstring>#include <iostream>#include <algorithm>#include <vector>#include <string>#include <stack>#include <queue>#include <deque>#include <map>#include <set>#include <bitset>typedef int INT;using std::cin;using std::cout;using std::endl;inline INT readIn(){ INT a = 0; bool minus = false; char ch = getchar(); while (!(ch == '-' || (ch >= '0' && ch <= '9'))) ch = getchar(); if (ch == '-') { minus = true; ch = getchar(); } while (ch >= '0' && ch <= '9') { a = a * 10 + (ch - '0'); ch = getchar(); } if (minus) a = -a; return a;}inline void printOut(INT x){ char buffer[15]; INT length = 0; bool minus = x < 0; if (minus) x = -x; do { buffer[length++] = x % 10 + '0'; x /= 10; } while (x); if (minus) buffer[length++] = '-'; do { putchar(buffer[--length]); } while (length); putchar('\n');}const INT maxn = 50005;INT n;INT a[2][maxn];INT in[maxn];INT seq[maxn];bool vis[maxn];INT ans;INT sub[maxn];void calc(){ memset(sub, 0, sizeof(sub)); for(int i = 1; i <= n; i++) { INT x = seq[i] - i; if(x <= 0) x += n; sub[x]++; ans = std::min(ans, n - sub[x]); }}void run(){ n = readIn(); for(int i = 1; i <= n; i++) { in[a[0][i] = readIn()]++; in[a[1][i] = readIn()]++; if(a[0][i] == i || a[1][i] == i || in[a[0][i]] > 2 || in[a[1][i]] > 2) { printOut(-1); return; } } INT person = 1; while(seq[0] < n) { seq[++seq[0]] = person; vis[person] = true; if(!vis[a[0][person]]) person = a[0][person]; else if (!vis[a[1][person]]) person = a[1][person]; else if(seq[0] < n) { printOut(-1); return; } } ans = n; calc(); std::reverse(seq + 1, seq + 1 + n); calc(); printOut(ans);}int main(){ run(); return 0;}
- NOIP 2005 Senior 3
- NOIP 2009 Senior 3
- NOIP 2011 Senior 3
- NOIP 2012 Senior 3
- NOIP 2015 Senior 3
- NOIP 2014 Senior 3
- NOIP 2013 Senior 3
- NOIP 2016 Senior 3
- NOIP 2003 Senior 3
- NOIP 2017 Senior 3
- NOIP 2009 Senior 1
- NOIP 2009 Senior 4
- NOIP 2011 Senior 2
- NOIP 2011 Senior 4
- NOIP 2011 Senior 5
- NOIP 2011 Senior 6
- NOIP 2012 Senior 2
- NOIP 2012 Senior 5
- volatile
- 最长公共子序列问题
- 《EffectiveC++》读书笔记(二)条款4-6
- 2017.11.01
- 4.2
- NOIP 2005 Senior 3
- Android语音录制和播放
- ubuntu16.04如何把菜单栏放到下面
- java可变参数
- Spring Boot中的缓存支持(一)注解配置与EhCache使用
- Jzoj5441【NOIP2017提高A组冲刺11.1】序列
- Character类,Integer类
- 指针的一个重要用途
- 解决 Package:linux-image-4.10.0-38-generic 软件包系统损坏的问题