由于博主没有BZOJ权限号, 是在洛咕做的题~
完成了13题(虽然有一半难题都是看题解的QAQ)剩下的题咕咕咕~~
Luogu3585 [POI2015]PIE
Solution
模拟, 按顺序搜索, 把搜索到的需要印却没有印的点 和 印章的第一个点重合, 并印上。
另外, 纸上需要印的点 和 印章上沾墨水的点用数组储存, 能加快很多
Code
1 #include<cstdio> 2 #include<cstring> 3 #include<algorithm> 4 #include<vector> 5 #define R register 6 using namespace std; 7 typedef pair<int, int> P; 8 9 const int N = 1e3 + 5; 10 11 int n, m, a, b; 12 int stx, sty; 13 char s[N], in[N][N], mp[N][N]; 14 15 vector<P> need, offer; 16 17 int jud(int x, int y) { 18 if (x <= 0 || y <= 0 || x > n || y > m) 19 return 0; 20 return 1; 21 } 22 23 #define X first 24 #define Y second 25 int col(int x, int y) { 26 for (R int i = 0, up = offer.size(); i < up; ++i) { 27 int onx = x + offer[i].X - stx, ony = y + offer[i].Y - sty; 28 if (!jud(onx, ony)) return 0; 29 if (mp[onx][ony] == '.') return 0; 30 mp[onx][ony] = '.'; 31 } 32 return 1; 33 } 34 35 int work() { 36 stx = sty = 0; 37 offer.clear(); need.clear(); 38 for (R int i = 1; i <= n; ++i) scanf("%s", mp[i] + 1); 39 for (R int i = 1; i <= a; ++i) scanf("%s", in[i] + 1); 40 for (R int i = 1; i <= n; ++i) 41 for (R int j = 1; j <= m; ++j) if (mp[i][j] == 'x') 42 need.push_back(P(i, j)); 43 for (R int i = 1; i <= a; ++i) 44 for (R int j = 1; j <= b; ++j) if (in[i][j] == 'x') { 45 offer.push_back(P(i, j)); 46 if (!stx) stx = i, sty = j; 47 } 48 49 for (int i = 0, up = need.size(); i < up; ++i) 50 if (mp[need[i].X][need[i].Y] == 'x') 51 if (!col(need[i].X, need[i].Y)) return 0; 52 return 1; 53 } 54 #undef X 55 #undef Y 56 57 int main() 58 { 59 int Q; scanf("%d", &Q); 60 for (; Q; Q--) { 61 scanf("%d%d%d%d", &n, &m, &a, &b); 62 if (work()) puts("TAK"); 63 else puts("NIE"); 64 } 65 }
Luogu3594[POI2015]WIL-Wilcze doły
Solution
单调队列, 将长度为 $d$ 的最大字段和加入队列, 并且队列内 字段和 单调递减
开个双指针 $i, j$ 表示要选择的最长的连续区间的两端。
随着 $i$ 增加,把新的 长度为$d$ 的子段和加入队列。
然后逐渐右移指针$j$, 直到找到第一个$<=p$的区间。 随着$j$增加, 把 队列内超出范围的子段和 弹出
Code
1 #include<cstdio> 2 #include<cstring> 3 #include<algorithm> 4 #define rd read() 5 #define ll long long 6 #define R register 7 using namespace std; 8 9 const int N = 2e6 + 5; 10 11 int n, p, d; 12 ll sum[N], a[N]; 13 ll q[N]; 14 15 ll read() { 16 ll X = 0, p = 1; char c = getchar(); 17 for (; c > '9' || c < '0'; c = getchar()) 18 if (c == '-') p = -1; 19 for (; c >= '0' && c <= '9'; c = getchar()) 20 X = X * 10 + c - '0'; 21 return X * p; 22 } 23 24 int main() 25 { 26 n = rd; p = rd; d = rd; 27 for (R int i = 1; i <= n; ++i) 28 a[i] = rd, sum[i] = sum[i - 1] + a[i]; 29 int l = 1, r = 0, ans = d; 30 for (int i = d, j = 0; i <= n; ++i) { 31 ll tmp = sum[i] - sum[i - d]; 32 while (l <= r && sum[q[r]] - sum[q[r] - d] <= tmp) r--; 33 q[++r] = i; 34 tmp = sum[q[l]] - sum[q[l] - d]; 35 while (sum[i] - sum[j] - tmp > p) { 36 j++; 37 while (l <= r && q[l] - d < j) l++; 38 tmp = sum[q[l]] - sum[q[l] - d]; 39 } 40 ans = max(ans, i - j); 41 } 42 printf("%d ", ans); 43 }
Luogu3586[POI2015]LOG
Solution
树状数组
先考虑怎样判断是否符合条件, 数列中 $>=s$的个数为$cnt$, 若剩余的$<s$的数的和 $>= (c-cnt)*s$ 即可满足条件
这样我们就需要知道数列中有多少个数$>=s$, 以及$<s$的数的和, 可以用两个树状数组维护.
最后一个点 开LL
Code
1 #include<cstdio> 2 #include<cstring> 3 #include<algorithm> 4 #define rd read() 5 #define ll long long 6 using namespace std; 7 8 const int N = 2e6 + 5; 9 10 ll a[N], n, m, tot; 11 ll cnt[N], ls[N], sum[N]; 12 13 struct node { 14 int typ, x; 15 ll y; 16 }pro[N]; 17 18 ll read() { 19 ll X = 0, p = 1; char c = getchar(); 20 for (; c > '9' || c < '0'; c = getchar()) 21 if (c == '-') p = -1; 22 for (; c >= '0' && c <= '9'; c = getchar()) 23 X = X * 10 + c - '0'; 24 return X * p; 25 } 26 27 int lowbit(int x) { 28 return x & -x; 29 } 30 31 template <typename T> 32 void add(int x, ll d, T *s) { 33 for (; x <= tot; x += lowbit(x)) 34 s[x] += d; 35 } 36 37 template <typename T> 38 T query(int x, T *s) { 39 T re = 0; 40 for (; x; x -= lowbit(x)) 41 re += s[x]; 42 return re; 43 } 44 45 int fd(ll x) { 46 return lower_bound(ls + 1, ls + 1 + tot, x) - ls; 47 } 48 49 int main() 50 { 51 n = rd; m = rd; 52 tot = 1; 53 for (int i = 1; i <= m; ++i) { 54 char ch = getchar(); 55 while (ch > 'Z' || ch < 'A') ch = getchar(); 56 if (ch == 'U') { 57 pro[i].typ = 1; pro[i].x = rd; pro[i].y = rd; 58 ls[++tot] = pro[i].y; 59 } 60 else { 61 pro[i].typ = 2; pro[i].x = rd; pro[i].y = rd; 62 } 63 } 64 sort(ls + 1, ls + 1 + tot); 65 tot = unique(ls + 1, ls + 1 + tot) - ls - 1; 66 for (int i = 1; i <= n; ++i) 67 add(1, 1, cnt); 68 for (int i = 1; i <= m; ++i) { 69 if (pro[i].typ == 1) { 70 int ch = fd(a[pro[i].x]); 71 add(ch, -1, cnt); 72 add(ch, -ls[ch], sum); 73 ch = fd(pro[i].y); 74 add(ch, 1, cnt); 75 add(ch, ls[ch], sum); 76 a[pro[i].x] = pro[i].y; 77 } 78 else { 79 int ch = fd(pro[i].y), num; 80 num = n - query(ch - 1, cnt); 81 ll tmp = (pro[i].x - num) * pro[i].y; 82 if (query(ch - 1, sum) >= tmp) 83 puts("TAK"); 84 else puts("NIE"); 85 } 86 } 87 }
Luogu3584[POI2015]LAS
Solution
环形DP
$S$ 表示第$i$个人 以及和他相邻的两个人吃哪边的食物, 例如$S$的二进制上有4, 就表示第$i-1$个人 吃右边的食物, 反之, 则吃左边的食物
设置状态$f[i][S]$ 表示第$i$ 个人, 他相邻的吃食物的情况 为$S$, 能否符合要求。
由于环形最后一个人会影响第一个人, 则先枚举 第一个人, 到最后一个人判断是否存在与第一个人状态相符的情况 符合要求。
Code
1 #include<cstdio> 2 #include<cstring> 3 #include<algorithm> 4 #define rd read() 5 #define db double 6 using namespace std; 7 8 const int N = 1e6 + 5; 9 10 int n, c[N], f[N][8], ans[N], path[N][8]; 11 12 int read() { 13 int X = 0, p = 1; char c = getchar(); 14 for (; c > '9' || c < '0'; c = getchar()) 15 if (c == '-') p = -1; 16 for (; c >= '0' && c <= '9'; c = getchar()) 17 X = X * 10 + c - '0'; 18 return X * p; 19 } 20 21 int ch(int x) { 22 return (x + n) % n; 23 } 24 25 int jud(int x, int S) { 26 int le, re, me; 27 le = (S >> 2) & 1; re = S & 1; me = (S >> 1) & 1; 28 db now = c[ch(x + me)], nxt; 29 if (!me && le) now /= 2; 30 if (me && !re) now /= 2; 31 nxt = c[ch(x + (!me))]; 32 if (me && le) nxt /= 2; 33 if (!me && !re) nxt /= 2; 34 if (nxt > now) return 0; 35 else return 1; 36 } 37 38 39 int work(int S) { 40 memset(f, 0, sizeof (f)); 41 f[0][S] = 1; 42 for (int i = 1; i < n; ++i) 43 for (int now = 0; now < 8; ++now) if (jud(i, now)) 44 for (int pre = 0; pre <= 4; pre += 4) if (f[i - 1][pre + (now >> 1)]) 45 f[i][now] = 1, path[i][now] = pre + (now >> 1); 46 if (!f[n - 1][S >> 1] && !f[n - 1][(S >> 1) + 4]) 47 return 0; 48 if (f[n - 1][S >> 1]) { 49 for (int now = S >> 1, i = n - 1; ~i; now = path[i][now], --i) 50 ans[i] = (now & 2) >> 1; 51 for (int i = 0; i < n; ++i) 52 printf("%d ", (ans[i] + i) % n + 1); 53 } 54 else { 55 for (int now = (S >> 1) + 4, i = n - 1; ~i; now = path[i][now], --i) 56 ans[i] = (now & 2) >> 1; 57 for (int i = 0; i < n; ++i) 58 printf("%d ", (ans[i] + i) % n + 1); 59 } 60 return 1; 61 } 62 63 int main() 64 { 65 n = rd; 66 for (int i = 0; i < n; ++i) 67 c[i] = rd; 68 for (int i = 0; i < 8; ++i) if(jud(0, i)) 69 if (work(i)) return 0; 70 puts("NIE"); 71 }
Luogu3588[POI2015]PUS
Solution
线段树优化建树+差分约束
我们最初的想法应该是 在区间$[L,R]$内 选中的数向 未被选中的数连一条长度为$1$的边, 一次操作便有$N^2$条边, 这样肯定会MLE+TLE
于是我们又想到另外建一个虚点, 被选中的数向虚点建一条长度为$1$ 的边, 虚点再向未被选中的数 连长度为0的边, 这样一次操作便有$N$条边, 仍会MLE+TLE
于是我们用线段树优化建图, 所有操作中 区间约有$k+1$段, 所以虚点向区间连边, 被选中的点再向虚点连边。 就解决了这个问题。
最后再差分约束一下。
Code
1 #include<cstdio> 2 #include<cstring> 3 #include<algorithm> 4 #include<queue> 5 #define rd read() 6 using namespace std; 7 8 const int N = 4e5 + 5; 9 10 int head[N], tot; 11 int n, dis[N], m, p, vis[N], r[N]; 12 int pre[N]; 13 14 struct edge { 15 int nxt, to, w; 16 }e[N << 2]; 17 18 queue<int> q; 19 20 int read() { 21 int X = 0, p = 1; char c = getchar(); 22 for (; c > '9' || c < '0'; c = getchar()) 23 if (c == '-') p = -1; 24 for (; c >= '0' && c <= '9'; c = getchar()) 25 X = X * 10 + c - '0'; 26 return X * p; 27 } 28 29 void add(int u, int v, int w) { 30 e[++tot].to = v; 31 e[tot].nxt = head[u]; 32 e[tot].w = w; 33 r[v]++; 34 head[u] = tot; 35 } 36 37 namespace SegT { 38 int lc[N], rc[N], cnt, root; 39 #define mid ((l + r) >> 1) 40 41 void build(int &x, int l, int r) { 42 if (l == r) { 43 x = l; return; 44 } 45 x = ++cnt; 46 build(lc[x], l, mid); 47 build(rc[x], mid + 1, r); 48 add(lc[x], x, 0); 49 add(rc[x], x, 0); 50 } 51 52 void update(int L, int R, int c, int l, int r, int x) { 53 if (L > R) return; 54 if (L <= l && r <= R) { 55 add(x, c, 0); return; 56 } 57 if (mid >= L) 58 update(L, R, c, l, mid, lc[x]); 59 if (mid < R) 60 update(L, R, c, mid + 1, r, rc[x]); 61 } 62 }using namespace SegT; 63 64 void cmax(int &A, int B) { 65 if (A < B) 66 A = B; 67 } 68 69 void Topsort() { 70 for (int i = 1; i <= cnt; ++i) { 71 if (!dis[i]) dis[i] = 1; 72 if (!r[i]) q.push(i); 73 } 74 for (int u; !q.empty();) { 75 u = q.front(); q.pop(); 76 vis[u] = 1; 77 for (int i = head[u]; i; i = e[i].nxt) { 78 int nt = e[i].to; 79 cmax(dis[nt], dis[u] + e[i].w); 80 if (!(--r[nt])) q.push(nt); 81 } 82 } 83 } 84 85 int main() 86 { 87 cnt = n = rd; p = rd; m = rd; 88 for (int i = 1; i <= p; ++i) { 89 int pos = rd, x = rd; 90 pre[pos] = dis[pos] = x; 91 } 92 build(root, 1, n); 93 for (; m; m--) { 94 int l = rd, r = rd, num = rd; 95 int last = l, now; 96 ++cnt; 97 for (; num; --num) { 98 add(cnt, now = rd, 1); 99 update(last, now - 1, cnt, 1, n, root); 100 last = now + 1; 101 } 102 update(now + 1, r, cnt, 1, n, root); 103 } 104 Topsort(); 105 for (int i = 1; i <= n; ++i) 106 if (!vis[i] || dis[i] > 1e9 || (dis[i] > pre[i] && pre[i])) 107 return puts("NIE"), 0; 108 puts("TAK"); 109 for (int i = 1; i <= n; ++i) 110 printf("%d ", dis[i]); 111 }
Luogu3596[POI2015]MOD
Solution
树形DP
确实有难度啊QAQ
要求出每个子树的直径, 以及删去子树后剩下的那棵树的直径。
要使合并后的树直径最小, 需要把两棵树的直径的中点连起来, 设两个直径分别为 $f, g$最后得到的直径为 $max{f, g, (f+1)/2+(g+1)/2+1}$
要使合并后的树直径最大, 则把直径两端给连起来, 为$f+g+1$
状态转移不好讲, 就讲变量的意义, 具体看代码里的两个$dp$
$f[i]$ 表示 以$i$为根的子树的直径
$w[i][0]$ 表示以 $i$的子节点 为根的子树的直径中 最大的直径
$w[i][1]$ 表示以 $i$的子节点 为根的子树的直径中 第二大的直径
$d[i][0]$ 表示从 $i$ 出发 往下 的最长链的长度
$d[i][1]$ 和 $d[i][2]$ 依次类推
$line[i]$ 表示 从$i$开始, 到 除$i$外的子节点 所能得到的最长链
$g[i]$ 表示删去 以$i$ 为节点的子树后 得到的树 的直径
最后要得到方案 :
直径最长则 $bfs$ 求出两条直径的端点
直径最短, 则先求出两条直径的端点, 然后往上跳, 找到直径的中点
总复杂度$O(N)$
Code
1 #include<cstdio> 2 #include<cstring> 3 #include<algorithm> 4 #include<queue> 5 #define rd read() 6 const int N = 5e5 + 5; 7 using namespace std; 8 9 int f[N], g[N], d[N][3], w[N][2], line[N], n, fa[N]; 10 int head[N], tot; 11 int ansmax, ansmin = N, maxx, maxy, minx, miny; 12 int diax1, diay1, diax2, diay2; 13 int dep[N], vis[N]; 14 15 queue<int> q; 16 17 struct edge { 18 int nxt, to; 19 }e[N << 1]; 20 21 int read() { 22 int X = 0, p = 1; char c = getchar(); 23 for (; c > '9' || c < '0'; c = getchar()) 24 if (c == '-') p = -1; 25 for (; c >= '0' && c <= '9'; c = getchar()) 26 X = X * 10 + c - '0'; 27 return X * p; 28 } 29 30 void add(int u, int v) { 31 e[++tot].to = v; 32 e[tot].nxt = head[u]; 33 head[u] = tot; 34 } 35 36 void cmax(int &A, int B) { 37 if (A < B) 38 A = B; 39 } 40 41 void cmin(int &A, int B) { 42 if (A > B) 43 A = B; 44 } 45 46 void dp1(int u) { 47 for (int i = head[u]; i; i = e[i].nxt) { 48 int nt = e[i].to; 49 if (nt == fa[u]) continue; 50 dep[nt] = dep[u] + 1; 51 fa[nt] = u; 52 dp1(nt); 53 cmax(f[u], f[nt]); 54 int tmp = d[nt][0] + 1; 55 if (tmp > d[u][0]) 56 d[u][2] = d[u][1], d[u][1] = d[u][0], d[u][0] = tmp; 57 else if (tmp > d[u][1]) 58 d[u][2] = d[u][1], d[u][1] = tmp; 59 else if (tmp > d[u][2]) 60 d[u][2] = tmp; 61 tmp = f[nt]; 62 if (tmp > w[u][0]) 63 w[u][1] = w[u][0], w[u][0] = tmp; 64 else if (tmp > w[u][1]) 65 w[u][1] = tmp; 66 } 67 cmax(f[u], d[u][0] + d[u][1]); 68 } 69 70 void dp2(int u) { 71 if (u != 1) { 72 if (ansmax < g[u] + f[u] + 1) { 73 ansmax = g[u] + f[u] + 1; 74 maxx = fa[u]; maxy = u; 75 } 76 int res = max(g[u], f[u]); 77 cmax(res, (g[u] + 1) / 2 + (f[u] + 1) / 2 + 1); 78 if (ansmin > res) { 79 ansmin = res; 80 minx = fa[u]; miny = u; 81 } 82 } 83 for (int i = head[u]; i; i = e[i].nxt) { 84 int nt = e[i].to; 85 if (nt == fa[u]) continue; 86 cmax(line[nt], line[u] + 1); 87 cmax(g[nt], g[u]); 88 int tmp = d[nt][0] + 1; 89 if (tmp == d[u][0]) { 90 cmax(g[nt], d[u][1] + d[u][2]); 91 cmax(g[nt], line[u] + d[u][1]); 92 cmax(line[nt], d[u][1] + 1); 93 } 94 else if (tmp == d[u][1]) { 95 cmax(g[nt], d[u][0] + d[u][2]); 96 cmax(g[nt], line[u] + d[u][0]); 97 cmax(line[nt], d[u][0] + 1); 98 } 99 else { 100 cmax(g[nt], d[u][0] + d[u][1]); 101 cmax(g[nt], line[u] + d[u][0]); 102 cmax(line[nt], d[u][0] + 1); 103 } 104 tmp = f[nt]; 105 if (tmp == w[u][0]) 106 cmax(g[nt], w[u][1]); 107 else cmax(g[nt], w[u][0]); 108 dp2(nt); 109 } 110 } 111 112 void outputmax() { 113 memset(vis, 0, sizeof(vis)); 114 printf("%d %d %d", ansmax, maxx, maxy); 115 q.push(maxx); 116 vis[maxx] = 1; 117 for (int u; !q.empty();) { 118 u = q.front(); q.pop(); 119 maxx = u; 120 for (int i = head[u]; i; i = e[i].nxt) { 121 int nt = e[i].to; 122 if (nt == maxx || nt == maxy) continue; 123 if (vis[nt]) continue; 124 q.push(nt); 125 vis[nt] = 1; 126 } 127 } 128 q.push(maxy); 129 vis[maxy] = 1; 130 for (int u; !q.empty();) { 131 u = q.front(); q.pop(); 132 maxy = u; 133 for (int i = head[u]; i; i = e[i].nxt) { 134 int nt = e[i].to; 135 if (vis[nt]) continue; 136 q.push(nt); 137 vis[nt] = 1; 138 } 139 } 140 printf(" %d %d ", maxx, maxy); 141 } 142 143 int bfs(int S) { 144 memset(vis, 0, sizeof(vis)); 145 q.push(S); 146 vis[S] = 1; 147 int re; 148 for (int u; !q.empty();) { 149 u = q.front(); q.pop(); 150 re = u; 151 for (int i = head[u]; i; i = e[i].nxt) { 152 int nt = e[i].to; 153 if ((u == minx && nt == miny) || (u == miny && nt == minx)) 154 continue; 155 if (vis[nt]) continue; 156 q.push(nt); 157 vis[nt] = 1; 158 } 159 } 160 return re; 161 } 162 163 int solve(int x, int y, int len) { 164 int rest = len; 165 if (dep[x] < dep[y]) swap(x, y); 166 while (rest != (len + 1) / 2) 167 x = fa[x], rest --; 168 return x; 169 } 170 171 int main() 172 { 173 n = rd; 174 for (int i = 1; i < n; ++i) { 175 int u = rd, v = rd; 176 add(u, v); add(v, u); 177 } 178 dp1(1); dp2(1); 179 printf("%d %d %d", ansmin, minx, miny); 180 diax1 = bfs(minx); diay1 = bfs(diax1); 181 diax2 = bfs(miny); diay2 = bfs(diax2); 182 printf(" %d %d ", solve(diax1, diay1, g[miny]), solve(diax2, diay2, f[miny])); 183 outputmax(); 184 }
Luogu3592 [POI2015]MYJ
区间DP
Luogu3590[POI2015]TRZ
Solution
若子串中只存在一种字符 : $O(N)$扫一遍即可得到答案
其他情况 : 设sum[i][k] 为前$i$ 个字符中, 字符$k$的个数
若一个子串$[j + 1, i]$满足题设条件 , 则
$sum[i][1] - sum[j][1] != sum[i][2] - sum[j][2]$
$sum[i][2] - sum[j][2] != sum[i][3] - sum[j][3]$
$sum[i][3] - sum[j][3] != sum[i][1] - sum[j][1]$
移项得到 :
$sum[i][1] - sum[i][2] != sum[j][1] - sum[j][2]$
$sum[i][2] - sum[i][3] != sum[j][2] - sum[j][3]$
$sum[i][3] - sum[i][1] != sum[j][3] - sum[j][1]$
设 $x = sum[i][1] - sum[i][2], y = sum[i][2] - sum[i][3], z = sum[i][3] - sum[i][1]$
则要满足 $x_i != x_j, y_i != y_j, z_i != z_j$
有三维, 直接求解会很麻烦。
用线段树或者树状数组维护(我不会用树状数组QAQ)
首先对$x$进行排序, 相同的$x$一起查询更新, 这样就少了一维
然后以 $y$ 为线段树的下标,
节点内存储区间的 最小的标号 $i$ ,次小标号,最小标号的 $z$ 值, 最大标号, 次大标号, 最大标号的$z$值
最后吸氧才过的呜呜呜, 常数太大了
Code
1 // luogu-judger-enable-o2 2 #include<cstdio> 3 #include<cstring> 4 #include<algorithm> 5 #define rd read() 6 #define N 1000005 7 #define inf 1e8 8 #define ri register 9 using namespace std; 10 11 int n, sum[4], ans, ls[N], tot; 12 char op[N]; 13 14 struct P { 15 int id, x, y, z; 16 }a[N]; 17 18 inline int read() { 19 int X = 0, p = 1; char c = getchar(); 20 for (; c > '9' || c < '0'; c = getchar()) 21 if (c == '-') p = -1; 22 for (; c >= '0' && c <= '9'; c = getchar()) 23 X = X * 10 + c - '0'; 24 return X * p; 25 } 26 27 inline int cmp(const P &A, const P &B) { 28 return A.x < B.x; 29 } 30 31 inline void cmax(int &A, int B) { 32 if (A < B) 33 A = B; 34 } 35 36 namespace SegT { 37 #define mid ((l + r) >> 1) 38 #define lson x << 1 39 #define rson x << 1 | 1 40 struct node { 41 int maxz, minz, maxn[2], minn[2]; 42 node () { 43 maxn[0] = maxn[1] = -inf; 44 minn[0] = minn[1] = inf; 45 maxz = minz = inf; 46 } 47 }pt[N << 2]; 48 49 inline void up(node&x, int val, int z) { 50 if (val > x.maxn[0] && z != x.maxz) { 51 x.maxn[1] = x.maxn[0]; 52 x.maxn[0] = val; 53 x.maxz = z; 54 } 55 else if (val > x.maxn[0]&& z == x.maxz) { 56 x.maxn[0] = val; 57 x.maxz = z; 58 } 59 else if (val > x.maxn[1] && z != x.maxz) { 60 x.maxn[1] = val; 61 } 62 } 63 64 inline void down(node &x, int val, int z) { 65 if (val < x.minn[0] && z != x.minz) { 66 x.minn[1] = x.minn[0]; 67 x.minn[0] = val; 68 x.minz = z; 69 } 70 else if (val < x.minn[0] && z == x.minz) { 71 x.minn[0] = val; 72 x.minz = z; 73 } 74 else if (val < x.minn[1] && z != x.minz) { 75 x.minn[1] = val; 76 } 77 } 78 79 node merge(node l, node r) { 80 node res = l; 81 up(res, r.maxn[0], r.maxz); 82 up(res, r.maxn[1], inf); 83 down(res, r.minn[0], r.minz); 84 down(res, r.minn[1], inf); 85 return res; 86 } 87 88 inline void pushup(int x) { 89 pt[x] = merge(pt[lson], pt[rson]); 90 } 91 92 void modify(int pos, int val, int z, int l, int r, int x) { 93 if (l == r) { 94 up(pt[x], val, z); down(pt[x], val, z); 95 return; 96 } 97 if (pos <= mid) 98 modify(pos, val, z, l, mid, lson); 99 else modify(pos, val, z, mid + 1, r, rson); 100 pushup(x); 101 } 102 103 node query(int L, int R, int l, int r, int x) { 104 if (L <= l && r <= R) 105 return pt[x]; 106 node res, ltmp, rtmp; 107 if (L > R) return res; 108 if (mid >= L) 109 ltmp = query(L, R, l, mid, lson); 110 if (mid < R) 111 rtmp = query(L, R, mid + 1, r, rson); 112 res = merge(ltmp, rtmp); 113 return res; 114 } 115 }using namespace SegT; 116 117 int main() 118 { 119 n = rd; tot = 1; 120 scanf("%s", op + 1); 121 for (ri int i = 1, last = 0; i <= n; ++i) { 122 int typ; 123 if (op[i] == 'B') typ = 1; 124 else if (op[i] == 'C') typ = 2; 125 else typ = 3; 126 sum[typ]++; 127 a[i].id = i; 128 a[i].x = sum[1] - sum[2]; 129 a[i].y = sum[2] - sum[3]; 130 a[i].z = sum[3] - sum[1]; 131 ls[++tot] = a[i].y; 132 if (typ == last) sum[0]++; 133 else sum[0] = 1; 134 cmax(ans, sum[0]); 135 } 136 a[0].x = a[0].y = a[0].z = a[0].id = 0; 137 sort(a, a + 1 + n, cmp); 138 sort(ls + 1, ls + 1 + tot); 139 tot = unique(ls + 1, ls + 1 + tot) - ls - 1; 140 for (ri int i = 0; i <= n; ++i) 141 a[i].y = lower_bound(ls + 1, ls + 1 + tot, a[i].y) - ls; 142 a[n + 1].x = inf; 143 for (ri int i = 0, j = 0; i <= n; i = j + 1) { 144 for (j = i; j <= n && a[j].x == a[j + 1].x; ++j) { 145 node ltmp = query(1, a[j].y - 1, 1, tot, 1); 146 node rtmp = query(a[j].y + 1, tot, 1, tot, 1); 147 node tmp = merge(ltmp, rtmp); 148 if (tmp.maxz == a[j].z) 149 cmax(ans, tmp.maxn[1] - a[j].id); 150 else cmax(ans, tmp.maxn[0] - a[j].id); 151 if (tmp.minz == a[j].z) 152 cmax(ans, a[j].id - tmp.minn[1]); 153 else cmax(ans, a[j].id - tmp.minn[0]); 154 } 155 156 node ltmp = query(1, a[j].y - 1, 1, tot, 1); 157 node rtmp = query(a[j].y + 1, tot, 1, tot, 1); 158 node tmp = merge(ltmp, rtmp); 159 if (tmp.maxz == a[j].z) 160 cmax(ans, tmp.maxn[1] - a[j].id); 161 else cmax(ans, tmp.maxn[0] - a[j].id); 162 if (tmp.minz == a[j].z) 163 cmax(ans, a[j].id - tmp.minn[1]); 164 else cmax(ans, a[j].id - tmp.minn[0]); 165 166 for (j = i; j <= n && a[j].x == a[j + 1].x; ++j) { 167 modify(a[j].y, a[j].id, a[j].z, 1, tot, 1); 168 } 169 modify(a[j].y, a[j].id, a[j].z, 1, tot, 1); 170 } 171 printf("%d ", ans); 172 }
Luogu3591[POI2015]ODW
传送门~~
Luogu3597[POI2015]WYC
传送门~~
Luogu3587[POI2015]POD
不出意外又去看题解了, 是比较套路和有思维的题目
大佬写的题解简单易懂: 传送门 (https://www.cnblogs.com/five20/p/9581552.html) byfive20
环形前缀和, 用hash记录每种颜色出现的次数, 在某种颜色的最后一个位置删去该颜色的全部贡献。
两个hash值相同的位置就是可以分割的点。 因为要$r-l$ 尽可能接近mid ,能用单调队列来维护。
Code
1 #include<cstdio> 2 #include<algorithm> 3 #include<cstring> 4 #define rd read() 5 #define ll long long 6 #define R register 7 using namespace std; 8 9 const int N = 1000005, base1 = 999979, base2 = 999983, mod1 = 1e9 + 7, mod2 = 1e9 + 9; 10 11 ll po1[N], po2[N], sum1, sum2; 12 int last[N], cnt[N], n, k, a[N]; 13 14 struct node { 15 int id; 16 ll s1, s2; 17 }b[N]; 18 19 inline int read() { 20 int X = 0, p = 1; char c = getchar(); 21 for (; c > '9' || c < '0'; c = getchar()) 22 if (c == '-') p = -1; 23 for (; c >= '0' && c <= '9'; c = getchar()) 24 X = X * 10 + c - '0'; 25 return X * p; 26 } 27 28 int cmp(const node &A, const node &B) { 29 if (A.s1 != B.s1) return A.s1 < B.s1; 30 if (A.s2 != B.s2) return A.s2 < B.s2; 31 return A.id < B.id; 32 } 33 34 int jud(int x, int y) { 35 if (b[x].s1 != b[y].s1) return 0; 36 if (b[x].s2 != b[y].s2) return 0; 37 return 1; 38 } 39 40 void cmin(int &A, int B) { 41 if (A > B) A = B; 42 } 43 44 int Abs(int A) { 45 return A > 0 ? A : -A; 46 } 47 48 int main() 49 { 50 n = rd; k = rd; 51 for (R int i = 1; i <= n; ++i) 52 a[i] = rd; 53 po1[0] = po2[0] = 1; 54 for (R int i = 1; i <= k; ++i) 55 po1[i] = po1[i - 1] * base1 % mod1, 56 po2[i] = po2[i - 1] * base2 % mod2; 57 for (R int i = 1; i <= n; ++i) 58 cnt[a[i]]++, last[a[i]] = i; 59 for (R int i = 1; i <= n; ++i) { 60 (sum1 += po1[a[i]]) %= mod1; 61 (sum2 += po2[a[i]]) %= mod2; 62 if (i == last[a[i]]) 63 sum1 = (sum1 - po1[a[i]] * cnt[a[i]] % mod1) % mod1, 64 sum1 = (sum1 + mod1) % mod1, 65 sum2 = (sum2 - po2[a[i]] * cnt[a[i]] % mod2) % mod2, 66 sum2 = (sum2 + mod2) % mod2; 67 b[i].id = i, 68 b[i].s1 = sum1, 69 b[i].s2 = sum2; 70 } 71 sort(b + 1, b + 1 + n, cmp); 72 ll ans1 = 0; int ans2 = n; 73 int mid = (n + 1) >> 1; 74 for (int i = 1, j = 1; i <= n; i = j) { 75 while (j <= n && jud(i, j)) j++; 76 ans1 += 1LL * (j - i) * (j - i - 1) / 2; 77 for (int l = i, r = i; r < j; ++r) { 78 while (l < r && b[r].id - b[l].id >= mid) l++; 79 cmin(ans2, Abs(n - 2 * (b[r].id - b[l].id))); 80 if (l != i) 81 cmin(ans2, Abs(n - 2 * (b[r].id - b[l - 1].id))); 82 } 83 } 84 printf("%lld %d ", ans1, ans2); 85 }