@bzoj - 4382@ [POI2015] Podział naszyjnika
@description@
长度为 n 的一串项链,每颗珠子是 k 种颜色之一。 第 i 颗与第 i-1,i+1 颗珠子相邻,第 n 颗与第 1 颗也相邻。
切两刀,把项链断成两条链。要求每种颜色的珠子只能出现在其中一条链中。
求方案数量(保证至少存在一种),以及切成的两段长度之差绝对值的最小值。
input
第一行 n, k (2<=k<=n<=1000000)。颜色从 1 到 k 标号。
接下来 n 个数,按顺序表示每颗珠子的颜色。(保证 k 种颜色各出现至少一次)。
output
一行两个整数:方案数量,和长度差的最小值
sample input
9 5
2 5 3 2 2 4 1 1 3
sample output
4 3
sample explain
四种方法中较短的一条分别是 (5), (4), (1,1), (4,1,1)。相差最小值 6 - 3 = 3。
@solution@
网上一查题解,woc,怎么都是用哈希做的啊。
还看到用什么优先队列过的……看不懂看不懂。
但是怎么都没有人跟我想的一个算法啊……
首先我们转换一下问题:
一个区间 [l, r] 是合法的当且仅当每种颜色要么在这个区间里面全部出现,要么在这个区间里面全部不出现。求合法区间数以及 |区间长度*2 - n| 的最小值(需要保证 l ≠ 1,不然会重复计算)。
这样就可以把环形序列转为线性序列。
然后分析一下合法区间的性质:
性质(1):如果 [a, b] 与 [c, d] 都是合法区间且 a <= c <= b <= d,则 [c, b] 是合法区间。
性质(2):如果 [a, b] 与 [c, b] 都是合法区间且 a < c <= b,则 [a, c - 1] 是合法区间;如果 [a, b] 与 [a, c] 都是合法区间且 a <= b < c,则 [b + 1, c] 是合法区间。
性质(3):如果 [a, b] 与 [b + 1, c] 都是合法区间且 a <= b < c,则 [a, c] 是合法区间。
都比较直观。
这些性质表明:我们可以把相交的合法区间转换成若干不相交的合法区间,也可以把连续的合法区间合并成更大的合法区间。
性质(4):一个合法区间的左端点,必然是每个颜色第一次出现的位置。
也比较直观。它给出了寻找合法区间的左端点的方法。
我们记 fir[i] 表示第 i 种颜色第一次出现的位置,记 lst[i] 表示第 i 种颜色最后一次出现的位置。
对于某一个左端点 fir[x],我们想要去寻找它合法的最小右端点。
首先它的右端点 >= lst[x]。我们记录一个当前的最小右端点 p,如果存在一个颜色 c,满足 fir[x] < fir[c] <= p < lst[c],则将 p 移动到 lst[c]。重复直到不存在这样的颜色为止。
最后判断是否存在一个颜色 c,满足 fir[c] < fir[x] < j <= p 且第 j 个位置也是颜色 c。此时我们可以判定以 fir[x] 为左端点不存在合法解。
可以发现,除了上面处理出来的区间和它们拼起来的区间以外不存在其他的合法区间。
因此我们就关注怎么实现上面的过程。如果直接实现是 O(n^2) 的。
对于第一步,我们将颜色按 fir[x] 从大到小排序,作一次扫描。
如果颜色 p 对应的最小区间包含颜色 q 对应的最小区间,则 q 这个区间在第一步的过程中没有 p 的限制大,我们可以直接丢掉 q。
于是我们可以在扫描的时候维护一个由不相交不包含的区间构成的栈。
如果新加入的区间包含栈顶区间,弹掉栈顶,继续循环。
如果新加入的区间与栈顶区间相交,将新加入的区间的右端点延伸到栈顶区间的右端点,弹掉栈顶,中止循环。
如果新加入的区间与栈顶区间没有交集,弹掉栈顶,中止循环。
这一步的时间复杂度为 O(nlog n)。
对于第二步,我们对于每个位置 i 记录 fir[这个位置上的颜色]。
查询区间最小值即可。时间复杂度也为 O(nlog n)。
对于第三部,即拼合区间。我们将连续的区间存成一个序列,存储它们的区间长度。
则拼合出来的区间对应这个序列里的某个连续的子段。
使用滑动窗口即可统计题目要求的信息。时间复杂度 O(n)。
故总时间复杂度 O(nlog n)。
@accepted code@
#include<cmath>
#include<stack>
#include<cstdio>
#include<vector>
#include<algorithm>
using namespace std;
const int MAXN = 1000000 + 5;
inline int abs(int x) {
return x >= 0 ? x : -x;
}
int ST[20][MAXN];
int rmq(int l, int r) {
int k = log2(r-l+1), p = (1<<k);
return min(ST[k][l], ST[k][r-p+1]);
}
struct segment{
int le, ri;
segment(int _l=0, int _r=0):le(_l), ri(_r){}
}seg[MAXN], arr[MAXN];
bool cmpl(segment a, segment b) {
return a.le < b.le;
}
bool cmpr(segment a, segment b) {
return a.ri < b.ri;
}
stack<segment>stk;
vector<int>vec[MAXN];
int mn[MAXN], mx[MAXN], f[MAXN], num[MAXN];
int main() {
int n, k; scanf("%d%d", &n, &k);
for(int i=1;i<=k;i++) mx[i] = 0, mn[i] = n + 1;
for(int i=1;i<=n;i++) {
int x; scanf("%d", &x);
mx[x] = max(mx[x], i), mn[x] = min(mn[x], i);
ST[0][i] = mn[x];
}
for(int i=1;i<=k;i++) seg[i] = segment(mn[i], mx[i]);
sort(seg + 1, seg + k + 1, cmpl);
int cnt = 0;
for(int i=k;i>=1;i--) {
segment p = seg[i];
while( !stk.empty() ) {
if( stk.top().ri <= p.ri )
arr[++cnt] = stk.top(), stk.pop();
else {
if( stk.top().le <= p.ri )
p.ri = stk.top().ri, stk.pop();
break;
}
}
stk.push(p);
}
while( !stk.empty() )
arr[++cnt] = stk.top(), stk.pop();
for(int i=1;i<=n+1;i++) f[i] = -1;
for(int i=1;i<21;i++)
for(int j=1;j+(1<<(i-1))<=n;j++)
ST[i][j] = min(ST[i-1][j], ST[i-1][j+(1<<(i-1))]);
for(int i=1;i<=cnt;i++) {
if( rmq(arr[i].le, arr[i].ri) < arr[i].le ) continue;
f[arr[i].le] = arr[i].ri;
}
long long ans1 = 0;
int ans2 = n, tot = 0;
for(int i=n;i>=2;i--) {
if( f[i] == -1 ) continue;
if( f[f[i] + 1] == -1 )
num[i] = (++tot);
else num[i] = num[f[i] + 1];
vec[num[i]].push_back(f[i] - i + 1);
}
for(int i=1;i<=tot;i++) {
ans1 += 1LL*(vec[i].size() + 1)*vec[i].size()/2;
int l = 0, s = 0;
for(int r=0;r<vec[i].size();r++) {
s = s + vec[i][r];
while( 2*s >= n ) {
ans2 = min(ans2, abs(2*s - n));
s = s - vec[i][l], l++;
}
ans2 = min(ans2, abs(2*s - n));
}
}
printf("%lld %d\n", ans1, ans2);
return 0;
}
@details@
好像……挺复杂的?其实主要是性质比较多啦。
我发现哈希做法和我这个做法在某些方面挺类似。
然后我好像多找了几个性质将不准确的算法(哈希的确无法保证正确性)转为一个准确的算法。
由于没有人写过这种做法的题解,如果这个做法有问题请务必留言告诉我。
@bzoj - 4382@ [POI2015] Podział naszyjnika的更多相关文章
- 【BZOJ4382】[POI2015]Podział naszyjnika 堆+并查集+树状数组
[BZOJ4382][POI2015]Podział naszyjnika Description 长度为n的一串项链,每颗珠子是k种颜色之一. 第i颗与第i-1,i+1颗珠子相邻,第n颗与第1颗也相 ...
- BZOJ4382 : [POI2015]Podział naszyjnika
对于每种颜色,可以发现可以切的位置被分割成了若干段独立的区域. 给每个区域一个编号,将$m$种颜色的情况当成字符串来看,如果两个切口的字符串完全匹配,那么可以在这里切两刀. 可以构造hash函数,通过 ...
- [BZOJ4382][POI2015]Podział naszyjnika (神奇HASH)
[问题描述] 长度为n 的一串项链,每颗珠子是K 种颜色之一.第i 颗与第i-1,i+1 颗珠子相邻,第n 颗与第1 颗也相邻. 切两刀,把项链断成两条链.要求每种颜色的珠子只能出现在其中 ...
- bzoj 4386: [POI2015]Wycieczki
bzoj 4386: [POI2015]Wycieczki 这题什么素质,爆long long就算了,连int128都爆……最后还是用long double卡过的……而且可能是我本身自带大常数吧,T了 ...
- BZOJ 4385: [POI2015]Wilcze doły
4385: [POI2015]Wilcze doły Time Limit: 10 Sec Memory Limit: 128 MBSubmit: 648 Solved: 263[Submit][ ...
- BZOJ 4384: [POI2015]Trzy wieże
4384: [POI2015]Trzy wieże Time Limit: 20 Sec Memory Limit: 128 MBSubmit: 217 Solved: 61[Submit][St ...
- Bzoj 3747: [POI2015]Kinoman 线段树
3747: [POI2015]Kinoman Time Limit: 60 Sec Memory Limit: 128 MBSubmit: 553 Solved: 222[Submit][Stat ...
- BZOJ 3747 POI2015 Kinoman 段树
标题效果:有m点,每个点都有一个权值.现在我们有这个m为点的长度n该序列,寻求区间,它仅出现一次在正确的点区间内值和最大 想了很久,甚至神标题,奔说是水的问题--我醉了 枚举左点 对于每个请求留点右键 ...
- BZOJ 4380 [POI2015]Myjnie | DP
链接 BZOJ 4380 题面 有n家洗车店从左往右排成一排,每家店都有一个正整数价格p[i]. 有m个人要来消费,第i个人会驶过第a[i]个开始一直到第b[i]个洗车店,且会选择这些店中最便宜的一个 ...
随机推荐
- hive-hbase表的建立
HIVE-HBASE表CDH4的时候不成熟select*的时候会查询不出(必须通过字段名称),CDH5已经修复这点需要注意 接口表hive部分一般不做join select会占性能,因为hbase接口 ...
- Leetcode24.Swap Nodes in Pairs两两交换链表中的节点
给定一个链表,两两交换其中相邻的节点,并返回交换后的链表. 示例: 给定 1->2->3->4, 你应该返回 2->1->4->3. 说明: 你的算法只能使用常数的 ...
- ubuntu安装django
sudo apt-get install python-django -y#django操作mysql数据库时还需要安装python-mysqldb驱动,当然mysql安装是必须的前提 sudo ap ...
- 洛谷3953 (NOIp2017) 逛公园——记忆化搜索+用栈判0环
题目:https://www.luogu.org/problemnew/show/P3953 因为K只有50,所以想到用dp[ cr ][ j ]表示在点cr.比最短路多走了 j 的方案数.(看了TJ ...
- 【CF Manthan, Codefest 17 A】Tom Riddle's Diary
[链接]h在这里写链接 [题意] 在这里写题意 [题解] /* Be careful. 二重循环枚举 */ [错的次数] 0 [反思] 在这了写反思 [代码] #include <bits/st ...
- 深入浅出Cocoa 之动态创建类【转】
在前文<深入浅出Cocoa之类与对象>一文中,我已经详细介绍了ObjC中的 Class 与 Object 的概念,今天我们来如何在运行 时动态创建类.下面这个函数就是应用前面讲到的Clas ...
- 【JZOJ4928】【NOIP2017提高组模拟12.18】A
题目描述 数据范围 对于100%的数据,n<=100000,1<=A[i]<=5000 =w= Ans=∏1ai 代码 #include<iostream> #inclu ...
- Spring → 03:核心机制
一.控制反转 1.1.控制反转的概念 (1).Inverse of Controller被称为控制反转或反向控制,其实真正体现的是“控制转移”.(2).所谓的控制指的是负责对象关系的指定.对象创建.初 ...
- oracle dbms_repcat_admin能带来什么安全隐患
如果一个用户能执行dbms_repcat_admin包,将获得极大的系统权限. 以下情况可能获得该包的执行权限: 1.在sys下grant execute on dbms_repcat_admin t ...
- Hdu 1867 KMP
题目链接 题目意思: 给出两个字符串a, b, 求最长的公共字串c, c是a的后缀,也是b的前缀. 本题没有具体说明哪个字符串是文本串和匹配串, 所以都要考虑 思路: 查找的时候, 当文本串结束的时候 ...