BZOJ 4384: [POI2015]Trzy wie?e

4384: [POI2015]Trzy wie?e

Time Limit: 20 Sec  Memory Limit: 128 MB
Submit: 217  Solved: 61
[Submit][Status][Discuss]

Description

给定一个长度为n的仅包含‘B‘、‘C‘、‘S‘三种字符的字符串,请找到最长的一段连续子串,使得这一段要么只有一种字符,要么有多种字符,但是没有任意两种字符出现次数相同。

Input

第一行包含一个正整数n(1<=n<=1000000),表示字符串的长度。
第二行一个长度为n的字符串。

Output

包含一行一个正整数,即最长的满足条件的子串的长度。

Sample Input

9

CBBSSBCSC

Sample Output

6

HINT

选择BSSBCS这个子串。

Source

鸣谢Claris

[Submit][Status][Discuss]

分析

OTZ Claris

OTZ LincHpin

OTZ MirrorGray

——下面开始正文——

子串只包含一种字符的情况特殊处理,一遍扫描就可以了。

首先将字符串转换称数字串,分别用0,1,2替代B,C,S。

设sum[i][j]表示前i(0<=i<=n)个数字中j(0<=j<=2)的出现次数,则题目要求可以翻译如下

选取一段[lt + 1, rt],满足:

sum[rt][0] - sum[lt][0] != sum[rt][1] - sum[lt][1]

sum[rt][0] - sum[lt][0] != sum[rt][2] - sum[lt][2]

sum[rt][1] - sum[lt][1] != sum[rt][2] - sum[lt][2]

从Claris大神那里学到的机智转换——通过交换等式两边的元素,将lt和rt分离。

sum[rt][0] - sum[rt][1] != sum[lt][0] - sum[lt][1]

sum[rt][0] - sum[rt][2] != sum[lt][0] - sum[lt][2]

sum[rt][1] - sum[rt][2] != sum[lt][1] - sum[lt][2]

这看起来就神清气爽多了,问题就转换成了——

每个点上有一个三元组(x,y,z),以及一个id,求对于每个点来说,和它的三元组完全不同的id相距最远的点。

将(x,y,z,id)按照x从小到大排序,这样就可以保证x的不同了。

然后把y当作树状数组下标维护id的最大、最小值,这样查询的时候避开自己y的地方就可以保证y也不同了。

可以z还可能相同,所以要维护次优解,且强制最优解和次优解的z不同。当最优解的z和当前点的z相同时,选择用次优解来更新即可。这样就保证z也不同了。

然而并不清楚Claris大神是怎么用64行写完的,但也不代表我写200+行就有多麻烦,至少不是很费脑子(才怪)。

代码

  1 #include <bits/stdc++.h>
  2
  3 const int N = 1000005;
  4 const int inf = 1e9 + 7;
  5
  6 int n, m;
  7
  8 int h[N];
  9
 10 char s[N];
 11
 12 int sum[3];
 13
 14 int answer;
 15
 16 struct Point
 17 {
 18     int id, x, y, z;
 19
 20     Point(void) {};
 21     Point(int i, int a, int b, int c)
 22     {
 23         id = i;
 24         x = a;
 25         y = b;
 26         z = c;
 27     }
 28
 29     friend bool operator < (const Point &a, const Point &b)
 30     {
 31         return a.x < b.x;
 32     }
 33 }p[N];
 34
 35 struct Pair
 36 {
 37     int val, pos;
 38
 39     Pair(void) {};
 40
 41     Pair(int a, int b)
 42     {
 43         val = a;
 44         pos = b;
 45     }
 46
 47     friend bool operator ^ (const Pair &a, const Pair &b)
 48     {
 49         return a.pos != b.pos;
 50     }
 51
 52     friend bool operator < (const Pair &a, const Pair &b)
 53     {
 54         return a.val < b.val;
 55     }
 56
 57     friend bool operator > (const Pair &a, const Pair &b)
 58     {
 59         return a.val > b.val;
 60     }
 61 };
 62
 63 struct MinMax
 64 {
 65     Pair min0;
 66     Pair min1;
 67     Pair max0;
 68     Pair max1;
 69
 70     MinMax(void)
 71     {
 72         min0 = Pair(inf, -inf);
 73         min1 = Pair(inf, -inf);
 74         max0 = Pair(-inf, -inf);
 75         max1 = Pair(-inf, -inf);
 76     }
 77
 78     MinMax(int val, int pos)
 79     {
 80         min0 = max0 = Pair(val, pos);
 81         min1 = Pair(inf, -inf);
 82         max1 = Pair(-inf, -inf);
 83     }
 84
 85     friend MinMax operator + (const MinMax &a, const MinMax &b)
 86     {
 87         MinMax r;
 88
 89         if (a.min0 < b.min0)
 90         {
 91             r.min0 = a.min0;
 92             if (a.min1 < b.min0)
 93                 r.min1 = a.min1;
 94             else if (a.min0 ^ b.min0)
 95                 r.min1 = b.min0;
 96             else if (a.min1 < b.min1)
 97                 r.min1 = a.min1;
 98             else
 99                 r.min1 = b.min1;
100         }
101         else
102         {
103             r.min0 = b.min0;
104             if (b.min1 < a.min0)
105                 r.min1 = b.min1;
106             else if (a.min0 ^ b.min0)
107                 r.min1 = a.min0;
108             else if (b.min1 < a.min1)
109                 r.min1 = b.min1;
110             else
111                 r.min1 = a.min1;
112         }
113
114
115         if (a.max0 > b.max0)
116         {
117             r.max0 = a.max0;
118             if (a.max1 > b.max0)
119                 r.max1 = a.max1;
120             else if (a.max0 ^ b.max0)
121                 r.max1 = b.max0;
122             else if (a.max1 > b.max1)
123                 r.max1 = a.max1;
124             else
125                 r.max1 = b.max1;
126         }
127         else
128         {
129             r.max0 = b.max0;
130             if (b.max1 > a.max0)
131                 r.max1 = b.max1;
132             else if (a.max0 ^ b.max0)
133                 r.max1 = a.max0;
134             else if (b.max1 > a.max1)
135                 r.max1 = b.max1;
136             else
137                 r.max1 = a.max1;
138         }
139
140         return r;
141     }
142 };
143
144 struct BIT
145 {
146     MinMax tree[N];
147
148     int lowbit(int x)
149     {
150         return x & -x;
151     }
152
153     MinMax query(int p)
154     {
155         MinMax r;
156
157         while (p)
158         {
159             r = r + tree[p];
160             p -= lowbit(p);
161         }
162
163         return r;
164     }
165
166     void insert(int p, MinMax v)
167     {
168         while (p <= m)
169         {
170             tree[p] = tree[p] + v;
171             p += lowbit(p);
172         }
173     }
174 }lt, rt;
175
176 signed main(void)
177 {
178     scanf("%d%s", &n, s + 1);
179
180     for (int i = 1, j = 0; i <= n; ++i)
181     {
182         if (s[i] == s[i - 1])
183             answer = std::max(answer, ++j);
184         else
185             answer = std::max(answer, j = 1);
186     }
187
188     for (int i = 1; i <= n; ++i)
189     {
190         switch (s[i])
191         {
192             case ‘B‘: ++sum[0]; break;
193             case ‘C‘: ++sum[1]; break;
194             case ‘S‘: ++sum[2]; break;
195         }
196
197         p[i] = Point(
198             i,
199             sum[0] - sum[1],
200             sum[1] - sum[2],
201             sum[2] - sum[0]
202         );
203
204         h[i] = p[i].y;
205     }
206
207     p[++n] = Point(0, 0, 0, 0);
208
209     std::sort(p + 1, p + 1 + n);
210     std::sort(h + 1, h + 1 + n);
211
212     m = std::unique(h + 1, h + 1 + n) - h - 1;
213
214     for (int i = 1; i <= n; ++i)
215         p[i].y = std::lower_bound(h + 1, h + 1 + m, p[i].y) - h;
216
217     for (int i = 1, j = 1; i <= n; i = j)
218     {
219         while (j <= n && p[i].x == p[j].x)++j;
220
221         for (int k = i; k < j; ++k)
222         {
223             MinMax l = lt.query(p[k].y - 1);
224             MinMax r = rt.query(m - p[k].y);
225
226             if (l.min0.pos != p[k].z)
227                 answer = std::max(answer, p[k].id - l.min0.val);
228             else
229                 answer = std::max(answer, p[k].id - l.min1.val);
230
231             if (l.max0.pos != p[k].z)
232                 answer = std::max(answer, l.max0.val - p[k].id);
233             else
234                 answer = std::max(answer, l.max1.val - p[k].id);
235
236             if (r.min0.pos != p[k].z)
237                 answer = std::max(answer, p[k].id - r.min0.val);
238             else
239                 answer = std::max(answer, p[k].id - r.min1.val);
240
241             if (r.max0.pos != p[k].z)
242                 answer = std::max(answer, r.max0.val - p[k].id);
243             else
244                 answer = std::max(answer, r.max1.val - p[k].id);
245         }
246
247         for (int k = i; k < j; ++k)
248         {
249             lt.insert(p[k].y, MinMax(p[k].id, p[k].z));
250             rt.insert(m + 1 - p[k].y, MinMax(p[k].id, p[k].z));
251         }
252     }
253
254     printf("%d\n", answer);
255 }

BZOJ_4384.cpp

@Author: YouSiki

时间: 2024-11-09 00:49:25

BZOJ 4384: [POI2015]Trzy wie?e的相关文章

bzoj4384[POI2015]Trzy wie?e

题意: 给定一个长度为n的仅包含'B'.'C'.'S'三种字符的字符串,请找到最长的一段连续子串,使得这一段要么只有一种字符,要么有多种字符,但是没有任意两种字符出现次数相同. 题解: 恶心的树状数组题.首先先求出只有一种字符的最长字串.然后预处理出前缀和.题目要求满足 cnt[1][i]-cnt[1][j]!=cnt[2][i]-cnt[2][j] cnt[2][i]-cnt[2][j]!=cnt[3][i]-cnt[3][j] cnt[1][i]-cnt[1][j]!=cnt[3][i]-c

bzoj 4385: [POI2015]Wilcze do?y

4385: [POI2015]Wilcze do?y Description 给定一个长度为n的序列,你有一次机会选中一段连续的长度不超过d的区间,将里面所有数字全部修改为0.请找到最长的一段连续区间,使得该区间内所有数字之和不超过p. Input 第一行包含三个整数n,p,d(1<=d<=n<=2000000,0<=p<=10^16).第二行包含n个正整数,依次表示序列中每个数w[i](1<=w[i]<=10^9). Output 包含一行一个正整数,即修改后能

BZOJ 3747: [POI2015]Kinoman( 线段树 )

线段树... 我们可以枚举左端点 , 然后用线段树找出所有右端点中的最大值 . ----------------------------------------------------------------------------------------- #include<cstdio> #include<algorithm> #include<cstring> #include<iostream> #define rep( i , n ) for( i

BZOJ 4380 [POI2015]Myjnie | DP

链接 BZOJ 4380 题面 有n家洗车店从左往右排成一排,每家店都有一个正整数价格p[i]. 有m个人要来消费,第i个人会驶过第a[i]个开始一直到第b[i]个洗车店,且会选择这些店中最便宜的一个进行一次消费.但是如果这个最便宜的价格大于c[i],那么这个人就不洗车了. 请给每家店指定一个价格,使得所有人花的钱的总和最大. Input 第一行包含两个正整数n,m(1<=n<=50,1<=m<=4000). 接下来m行,每行包含三个正整数a[i],b[i],ci Output 第

BZOJ 3750: [POI2015]Piecz?? 【模拟】

Description 一张n*m的方格纸,有些格子需要印成黑色,剩下的格子需要保留白色. 你有一个a*b的印章,有些格子是凸起(会沾上墨水)的.你需要判断能否用这个印章印出纸上的图案.印的过程中需要满足以下要求: (1)印章不可以旋转. (2)不能把墨水印到纸外面. (3)纸上的同一个格子不可以印多次. Input 第一行一个整数q(1<=q<=10),表示测试点数量. 接下来q个测试点,每个测试点中: 第一行包含4个整数n,m,a,b(1<=n,m,a,b<=1000). 接下

bzoj 4383: [POI2015]Pustynia

复习了一下线段树优化建图的姿势,在线段树上连边跑拓扑排序 这题竟然卡vector……丧病 #include <bits/stdc++.h> #define N 1810000 using namespace std; int s, n, m; int pi[N], vis[N]; int lt[N], nt[N], bi[N], ci[N]; int lp[N], np[N], bp[N]; int tl; int lb[N], rb[N], lc[N], rc[N]; int out[N],

BZOJ 3749: [POI2015]?asuchy【动态规划】

Description 圆桌上摆放着n份食物,围成一圈,第i份食物所含热量为c[i]. 相邻两份食物之间坐着一个人,共有n个人.每个人有两种选择,吃自己左边或者右边的食物.如果两个人选择了同一份食物,这两个人会平分这份食物,每人获得一半的热量. 假如某个人改变自己的选择后(其他n-1个人的选择不变),可以使自己获得比原先更多的热量,那么这个人会不满意. 请你给每个人指定应该吃哪一份食物,使得所有人都能够满意. Input 第一行一个整数n(2<=n<=1000000),表示食物的数量(即人数)

BZOJ 3747 POI2015 Kinoman

因为上午没有准备够题目,结果发现写完这道题没题可写了QAQ 又因为这道题范围是100w,我写了发线段树,以为要T,上午就花了一个小时拼命卡常数 结果下午一交居然过了QAQ 我们考虑枚举L,求最大R使得[L,R]是对于当前L最大权值的区间 考虑每个点的影响 如果从L向右他是第一个,那么他会对后面产生a[f[L]]的贡献 如果从L向右他是第二个,那么他会对后面产生-a[f[L]]的贡献 然后我们维护一棵线段树,每次查询最值并且进行更新即可 include<cstdio> #include<i

BZOJ 3747: [POI2015]Kinoman 【线段树】

Description 共有m部电影,编号为1~m,第i部电影的好看值为w[i]. 在n天之中(从1~n编号)每天会放映一部电影,第i天放映的是第f[i]部. 你可以选择l,r(1<=l<=r<=n),并观看第l,l+1,…,r天内所有的电影.如果同一部电影你观看多于一次,你会感到无聊,于是无法获得这部电影的好看值.所以你希望最大化观看且仅观看过一次的电影的好看值的总和. Input 第一行两个整数n,m(1<=m<=n<=1000000). 第二行包含n个整数f[1]