由于博主没有BZOJ权限号, 是在洛咕做的题~

完成了13题(虽然有一半难题都是看题解的QAQ)剩下的题咕咕咕~~

Luogu3585 [POI2015]PIE

Solution

模拟, 按顺序搜索, 把搜索到的需要印却没有印的点 和 印章的第一个点重合, 并印上。

另外, 纸上需要印的点 和 印章上沾墨水的点用数组储存, 能加快很多

Code

 #include<cstdio>
#include<cstring>
#include<algorithm>
#include<vector>
#define R register
using namespace std;
typedef pair<int, int> P; const int N = 1e3 + ; int n, m, a, b;
int stx, sty;
char s[N], in[N][N], mp[N][N]; vector<P> need, offer; int jud(int x, int y) {
if (x <= || y <= || x > n || y > m)
return ;
return ;
} #define X first
#define Y second
int col(int x, int y) {
for (R int i = , up = offer.size(); i < up; ++i) {
int onx = x + offer[i].X - stx, ony = y + offer[i].Y - sty;
if (!jud(onx, ony)) return ;
if (mp[onx][ony] == '.') return ;
mp[onx][ony] = '.';
}
return ;
} int work() {
stx = sty = ;
offer.clear(); need.clear();
for (R int i = ; i <= n; ++i) scanf("%s", mp[i] + );
for (R int i = ; i <= a; ++i) scanf("%s", in[i] + );
for (R int i = ; i <= n; ++i)
for (R int j = ; j <= m; ++j) if (mp[i][j] == 'x')
need.push_back(P(i, j));
for (R int i = ; i <= a; ++i)
for (R int j = ; j <= b; ++j) if (in[i][j] == 'x') {
offer.push_back(P(i, j));
if (!stx) stx = i, sty = j;
} for (int i = , up = need.size(); i < up; ++i)
if (mp[need[i].X][need[i].Y] == 'x')
if (!col(need[i].X, need[i].Y)) return ;
return ;
}
#undef X
#undef Y int main()
{
int Q; scanf("%d", &Q);
for (; Q; Q--) {
scanf("%d%d%d%d", &n, &m, &a, &b);
if (work()) puts("TAK");
else puts("NIE");
}
}

Luogu3585 PIE

Luogu3594[POI2015]WIL-Wilcze doły

Solution

单调队列, 将长度为 $d$ 的最大字段和加入队列, 并且队列内 字段和 单调递减

开个双指针 $i, j$ 表示要选择的最长的连续区间的两端。

随着 $i$ 增加,把新的 长度为$d$ 的子段和加入队列。

然后逐渐右移指针$j$, 直到找到第一个$<=p$的区间。 随着$j$增加, 把 队列内超出范围的子段和 弹出

Code

 #include<cstdio>
#include<cstring>
#include<algorithm>
#define rd read()
#define ll long long
#define R register
using namespace std; const int N = 2e6 + ; int n, p, d;
ll sum[N], a[N];
ll q[N]; ll read() {
ll X = , p = ; char c = getchar();
for (; c > '' || c < ''; c = getchar())
if (c == '-') p = -;
for (; c >= '' && c <= ''; c = getchar())
X = X * + c - '';
return X * p;
} int main()
{
n = rd; p = rd; d = rd;
for (R int i = ; i <= n; ++i)
a[i] = rd, sum[i] = sum[i - ] + a[i];
int l = , r = , ans = d;
for (int i = d, j = ; i <= n; ++i) {
ll tmp = sum[i] - sum[i - d];
while (l <= r && sum[q[r]] - sum[q[r] - d] <= tmp) r--;
q[++r] = i;
tmp = sum[q[l]] - sum[q[l] - d];
while (sum[i] - sum[j] - tmp > p) {
j++;
while (l <= r && q[l] - d < j) l++;
tmp = sum[q[l]] - sum[q[l] - d];
}
ans = max(ans, i - j);
}
printf("%d\n", ans);
}

Luogu3594 WIL-Wilcze doły

Luogu3586[POI2015]LOG

Solution

树状数组

先考虑怎样判断是否符合条件, 数列中 $>=s$的个数为$cnt$, 若剩余的$<s$的数的和 $>= (c-cnt)*s$ 即可满足条件

这样我们就需要知道数列中有多少个数$>=s$, 以及$<s$的数的和, 可以用两个树状数组维护.

最后一个点 开LL

Code

 #include<cstdio>
#include<cstring>
#include<algorithm>
#define rd read()
#define ll long long
using namespace std; const int N = 2e6 + ; ll a[N], n, m, tot;
ll cnt[N], ls[N], sum[N]; struct node {
int typ, x;
ll y;
}pro[N]; ll read() {
ll X = , p = ; char c = getchar();
for (; c > '' || c < ''; c = getchar())
if (c == '-') p = -;
for (; c >= '' && c <= ''; c = getchar())
X = X * + c - '';
return X * p;
} int lowbit(int x) {
return x & -x;
} template <typename T>
void add(int x, ll d, T *s) {
for (; x <= tot; x += lowbit(x))
s[x] += d;
} template <typename T>
T query(int x, T *s) {
T re = ;
for (; x; x -= lowbit(x))
re += s[x];
return re;
} int fd(ll x) {
return lower_bound(ls + , ls + + tot, x) - ls;
} int main()
{
n = rd; m = rd;
tot = ;
for (int i = ; i <= m; ++i) {
char ch = getchar();
while (ch > 'Z' || ch < 'A') ch = getchar();
if (ch == 'U') {
pro[i].typ = ; pro[i].x = rd; pro[i].y = rd;
ls[++tot] = pro[i].y;
}
else {
pro[i].typ = ; pro[i].x = rd; pro[i].y = rd;
}
}
sort(ls + , ls + + tot);
tot = unique(ls + , ls + + tot) - ls - ;
for (int i = ; i <= n; ++i)
add(, , cnt);
for (int i = ; i <= m; ++i) {
if (pro[i].typ == ) {
int ch = fd(a[pro[i].x]);
add(ch, -, cnt);
add(ch, -ls[ch], sum);
ch = fd(pro[i].y);
add(ch, , cnt);
add(ch, ls[ch], sum);
a[pro[i].x] = pro[i].y;
}
else {
int ch = fd(pro[i].y), num;
num = n - query(ch - , cnt);
ll tmp = (pro[i].x - num) * pro[i].y;
if (query(ch - , sum) >= tmp)
puts("TAK");
else puts("NIE");
}
}
}

Luogu3586 LOG

Luogu3584[POI2015]LAS

Solution

环形DP

$S$ 表示第$i$个人 以及和他相邻的两个人吃哪边的食物, 例如$S$的二进制上有4, 就表示第$i-1$个人 吃右边的食物, 反之, 则吃左边的食物

设置状态$f[i][S]$ 表示第$i$ 个人, 他相邻的吃食物的情况 为$S$, 能否符合要求。

由于环形最后一个人会影响第一个人, 则先枚举 第一个人, 到最后一个人判断是否存在与第一个人状态相符的情况 符合要求。

Code

 #include<cstdio>
#include<cstring>
#include<algorithm>
#define rd read()
#define db double
using namespace std; const int N = 1e6 + ; int n, c[N], f[N][], ans[N], path[N][]; int read() {
int X = , p = ; char c = getchar();
for (; c > '' || c < ''; c = getchar())
if (c == '-') p = -;
for (; c >= '' && c <= ''; c = getchar())
X = X * + c - '';
return X * p;
} int ch(int x) {
return (x + n) % n;
} int jud(int x, int S) {
int le, re, me;
le = (S >> ) & ; re = S & ; me = (S >> ) & ;
db now = c[ch(x + me)], nxt;
if (!me && le) now /= ;
if (me && !re) now /= ;
nxt = c[ch(x + (!me))];
if (me && le) nxt /= ;
if (!me && !re) nxt /= ;
if (nxt > now) return ;
else return ;
} int work(int S) {
memset(f, , sizeof (f));
f[][S] = ;
for (int i = ; i < n; ++i)
for (int now = ; now < ; ++now) if (jud(i, now))
for (int pre = ; pre <= ; pre += ) if (f[i - ][pre + (now >> )])
f[i][now] = , path[i][now] = pre + (now >> );
if (!f[n - ][S >> ] && !f[n - ][(S >> ) + ])
return ;
if (f[n - ][S >> ]) {
for (int now = S >> , i = n - ; ~i; now = path[i][now], --i)
ans[i] = (now & ) >> ;
for (int i = ; i < n; ++i)
printf("%d ", (ans[i] + i) % n + );
}
else {
for (int now = (S >> ) + , i = n - ; ~i; now = path[i][now], --i)
ans[i] = (now & ) >> ;
for (int i = ; i < n; ++i)
printf("%d ", (ans[i] + i) % n + );
}
return ;
} int main()
{
n = rd;
for (int i = ; i < n; ++i)
c[i] = rd;
for (int i = ; i < ; ++i) if(jud(, i))
if (work(i)) return ;
puts("NIE");
}

Luogu3584 LAS

Luogu3588[POI2015]PUS

Solution

线段树优化建树+差分约束

我们最初的想法应该是 在区间$[L,R]$内 选中的数向 未被选中的数连一条长度为$1$的边, 一次操作便有$N^2$条边, 这样肯定会MLE+TLE

于是我们又想到另外建一个虚点, 被选中的数向虚点建一条长度为$1$ 的边, 虚点再向未被选中的数 连长度为0的边, 这样一次操作便有$N$条边, 仍会MLE+TLE

于是我们用线段树优化建图, 所有操作中 区间约有$k+1$段, 所以虚点向区间连边, 被选中的点再向虚点连边。 就解决了这个问题。

最后再差分约束一下。

Code

 #include<cstdio>
#include<cstring>
#include<algorithm>
#include<queue>
#define rd read()
using namespace std; const int N = 4e5 + ; int head[N], tot;
int n, dis[N], m, p, vis[N], r[N];
int pre[N]; struct edge {
int nxt, to, w;
}e[N << ]; queue<int> q; int read() {
int X = , p = ; char c = getchar();
for (; c > '' || c < ''; c = getchar())
if (c == '-') p = -;
for (; c >= '' && c <= ''; c = getchar())
X = X * + c - '';
return X * p;
} void add(int u, int v, int w) {
e[++tot].to = v;
e[tot].nxt = head[u];
e[tot].w = w;
r[v]++;
head[u] = tot;
} namespace SegT {
int lc[N], rc[N], cnt, root;
#define mid ((l + r) >> 1) void build(int &x, int l, int r) {
if (l == r) {
x = l; return;
}
x = ++cnt;
build(lc[x], l, mid);
build(rc[x], mid + , r);
add(lc[x], x, );
add(rc[x], x, );
} void update(int L, int R, int c, int l, int r, int x) {
if (L > R) return;
if (L <= l && r <= R) {
add(x, c, ); return;
}
if (mid >= L)
update(L, R, c, l, mid, lc[x]);
if (mid < R)
update(L, R, c, mid + , r, rc[x]);
}
}using namespace SegT; void cmax(int &A, int B) {
if (A < B)
A = B;
} void Topsort() {
for (int i = ; i <= cnt; ++i) {
if (!dis[i]) dis[i] = ;
if (!r[i]) q.push(i);
}
for (int u; !q.empty();) {
u = q.front(); q.pop();
vis[u] = ;
for (int i = head[u]; i; i = e[i].nxt) {
int nt = e[i].to;
cmax(dis[nt], dis[u] + e[i].w);
if (!(--r[nt])) q.push(nt);
}
}
} int main()
{
cnt = n = rd; p = rd; m = rd;
for (int i = ; i <= p; ++i) {
int pos = rd, x = rd;
pre[pos] = dis[pos] = x;
}
build(root, , n);
for (; m; m--) {
int l = rd, r = rd, num = rd;
int last = l, now;
++cnt;
for (; num; --num) {
add(cnt, now = rd, );
update(last, now - , cnt, , n, root);
last = now + ;
}
update(now + , r, cnt, , n, root);
}
Topsort();
for (int i = ; i <= n; ++i)
if (!vis[i] || dis[i] > 1e9 || (dis[i] > pre[i] && pre[i]))
return puts("NIE"), ;
puts("TAK");
for (int i = ; i <= n; ++i)
printf("%d ", dis[i]);
}

Luogu3588 PUS

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

 #include<cstdio>
#include<cstring>
#include<algorithm>
#include<queue>
#define rd read()
const int N = 5e5 + ;
using namespace std; int f[N], g[N], d[N][], w[N][], line[N], n, fa[N];
int head[N], tot;
int ansmax, ansmin = N, maxx, maxy, minx, miny;
int diax1, diay1, diax2, diay2;
int dep[N], vis[N]; queue<int> q; struct edge {
int nxt, to;
}e[N << ]; int read() {
int X = , p = ; char c = getchar();
for (; c > '' || c < ''; c = getchar())
if (c == '-') p = -;
for (; c >= '' && c <= ''; c = getchar())
X = X * + c - '';
return X * p;
} void add(int u, int v) {
e[++tot].to = v;
e[tot].nxt = head[u];
head[u] = tot;
} void cmax(int &A, int B) {
if (A < B)
A = B;
} void cmin(int &A, int B) {
if (A > B)
A = B;
} void dp1(int u) {
for (int i = head[u]; i; i = e[i].nxt) {
int nt = e[i].to;
if (nt == fa[u]) continue;
dep[nt] = dep[u] + ;
fa[nt] = u;
dp1(nt);
cmax(f[u], f[nt]);
int tmp = d[nt][] + ;
if (tmp > d[u][])
d[u][] = d[u][], d[u][] = d[u][], d[u][] = tmp;
else if (tmp > d[u][])
d[u][] = d[u][], d[u][] = tmp;
else if (tmp > d[u][])
d[u][] = tmp;
tmp = f[nt];
if (tmp > w[u][])
w[u][] = w[u][], w[u][] = tmp;
else if (tmp > w[u][])
w[u][] = tmp;
}
cmax(f[u], d[u][] + d[u][]);
} void dp2(int u) {
if (u != ) {
if (ansmax < g[u] + f[u] + ) {
ansmax = g[u] + f[u] + ;
maxx = fa[u]; maxy = u;
}
int res = max(g[u], f[u]);
cmax(res, (g[u] + ) / + (f[u] + ) / + );
if (ansmin > res) {
ansmin = res;
minx = fa[u]; miny = u;
}
}
for (int i = head[u]; i; i = e[i].nxt) {
int nt = e[i].to;
if (nt == fa[u]) continue;
cmax(line[nt], line[u] + );
cmax(g[nt], g[u]);
int tmp = d[nt][] + ;
if (tmp == d[u][]) {
cmax(g[nt], d[u][] + d[u][]);
cmax(g[nt], line[u] + d[u][]);
cmax(line[nt], d[u][] + );
}
else if (tmp == d[u][]) {
cmax(g[nt], d[u][] + d[u][]);
cmax(g[nt], line[u] + d[u][]);
cmax(line[nt], d[u][] + );
}
else {
cmax(g[nt], d[u][] + d[u][]);
cmax(g[nt], line[u] + d[u][]);
cmax(line[nt], d[u][] + );
}
tmp = f[nt];
if (tmp == w[u][])
cmax(g[nt], w[u][]);
else cmax(g[nt], w[u][]);
dp2(nt);
}
} void outputmax() {
memset(vis, , sizeof(vis));
printf("%d %d %d", ansmax, maxx, maxy);
q.push(maxx);
vis[maxx] = ;
for (int u; !q.empty();) {
u = q.front(); q.pop();
maxx = u;
for (int i = head[u]; i; i = e[i].nxt) {
int nt = e[i].to;
if (nt == maxx || nt == maxy) continue;
if (vis[nt]) continue;
q.push(nt);
vis[nt] = ;
}
}
q.push(maxy);
vis[maxy] = ;
for (int u; !q.empty();) {
u = q.front(); q.pop();
maxy = u;
for (int i = head[u]; i; i = e[i].nxt) {
int nt = e[i].to;
if (vis[nt]) continue;
q.push(nt);
vis[nt] = ;
}
}
printf(" %d %d\n", maxx, maxy);
} int bfs(int S) {
memset(vis, , sizeof(vis));
q.push(S);
vis[S] = ;
int re;
for (int u; !q.empty();) {
u = q.front(); q.pop();
re = u;
for (int i = head[u]; i; i = e[i].nxt) {
int nt = e[i].to;
if ((u == minx && nt == miny) || (u == miny && nt == minx))
continue;
if (vis[nt]) continue;
q.push(nt);
vis[nt] = ;
}
}
return re;
} int solve(int x, int y, int len) {
int rest = len;
if (dep[x] < dep[y]) swap(x, y);
while (rest != (len + ) / )
x = fa[x], rest --;
return x;
} int main()
{
n = rd;
for (int i = ; i < n; ++i) {
int u = rd, v = rd;
add(u, v); add(v, u);
}
dp1(); dp2();
printf("%d %d %d", ansmin, minx, miny);
diax1 = bfs(minx); diay1 = bfs(diax1);
diax2 = bfs(miny); diay2 = bfs(diax2);
printf(" %d %d\n", solve(diax1, diay1, g[miny]), solve(diax2, diay2, f[miny]));
outputmax();
}

Luogu3596 MOD

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

 // luogu-judger-enable-o2
#include<cstdio>
#include<cstring>
#include<algorithm>
#define rd read()
#define N 1000005
#define inf 1e8
#define ri register
using namespace std; int n, sum[], ans, ls[N], tot;
char op[N]; struct P {
int id, x, y, z;
}a[N]; inline int read() {
int X = , p = ; char c = getchar();
for (; c > '' || c < ''; c = getchar())
if (c == '-') p = -;
for (; c >= '' && c <= ''; c = getchar())
X = X * + c - '';
return X * p;
} inline int cmp(const P &A, const P &B) {
return A.x < B.x;
} inline void cmax(int &A, int B) {
if (A < B)
A = B;
} namespace SegT {
#define mid ((l + r) >> 1)
#define lson x << 1
#define rson x << 1 | 1
struct node {
int maxz, minz, maxn[], minn[];
node () {
maxn[] = maxn[] = -inf;
minn[] = minn[] = inf;
maxz = minz = inf;
}
}pt[N << ]; inline void up(node&x, int val, int z) {
if (val > x.maxn[] && z != x.maxz) {
x.maxn[] = x.maxn[];
x.maxn[] = val;
x.maxz = z;
}
else if (val > x.maxn[]&& z == x.maxz) {
x.maxn[] = val;
x.maxz = z;
}
else if (val > x.maxn[] && z != x.maxz) {
x.maxn[] = val;
}
} inline void down(node &x, int val, int z) {
if (val < x.minn[] && z != x.minz) {
x.minn[] = x.minn[];
x.minn[] = val;
x.minz = z;
}
else if (val < x.minn[] && z == x.minz) {
x.minn[] = val;
x.minz = z;
}
else if (val < x.minn[] && z != x.minz) {
x.minn[] = val;
}
} node merge(node l, node r) {
node res = l;
up(res, r.maxn[], r.maxz);
up(res, r.maxn[], inf);
down(res, r.minn[], r.minz);
down(res, r.minn[], inf);
return res;
} inline void pushup(int x) {
pt[x] = merge(pt[lson], pt[rson]);
} void modify(int pos, int val, int z, int l, int r, int x) {
if (l == r) {
up(pt[x], val, z); down(pt[x], val, z);
return;
}
if (pos <= mid)
modify(pos, val, z, l, mid, lson);
else modify(pos, val, z, mid + , r, rson);
pushup(x);
} node query(int L, int R, int l, int r, int x) {
if (L <= l && r <= R)
return pt[x];
node res, ltmp, rtmp;
if (L > R) return res;
if (mid >= L)
ltmp = query(L, R, l, mid, lson);
if (mid < R)
rtmp = query(L, R, mid + , r, rson);
res = merge(ltmp, rtmp);
return res;
}
}using namespace SegT; int main()
{
n = rd; tot = ;
scanf("%s", op + );
for (ri int i = , last = ; i <= n; ++i) {
int typ;
if (op[i] == 'B') typ = ;
else if (op[i] == 'C') typ = ;
else typ = ;
sum[typ]++;
a[i].id = i;
a[i].x = sum[] - sum[];
a[i].y = sum[] - sum[];
a[i].z = sum[] - sum[];
ls[++tot] = a[i].y;
if (typ == last) sum[]++;
else sum[] = ;
cmax(ans, sum[]);
}
a[].x = a[].y = a[].z = a[].id = ;
sort(a, a + + n, cmp);
sort(ls + , ls + + tot);
tot = unique(ls + , ls + + tot) - ls - ;
for (ri int i = ; i <= n; ++i)
a[i].y = lower_bound(ls + , ls + + tot, a[i].y) - ls;
a[n + ].x = inf;
for (ri int i = , j = ; i <= n; i = j + ) {
for (j = i; j <= n && a[j].x == a[j + ].x; ++j) {
node ltmp = query(, a[j].y - , , tot, );
node rtmp = query(a[j].y + , tot, , tot, );
node tmp = merge(ltmp, rtmp);
if (tmp.maxz == a[j].z)
cmax(ans, tmp.maxn[] - a[j].id);
else cmax(ans, tmp.maxn[] - a[j].id);
if (tmp.minz == a[j].z)
cmax(ans, a[j].id - tmp.minn[]);
else cmax(ans, a[j].id - tmp.minn[]);
} node ltmp = query(, a[j].y - , , tot, );
node rtmp = query(a[j].y + , tot, , tot, );
node tmp = merge(ltmp, rtmp);
if (tmp.maxz == a[j].z)
cmax(ans, tmp.maxn[] - a[j].id);
else cmax(ans, tmp.maxn[] - a[j].id);
if (tmp.minz == a[j].z)
cmax(ans, a[j].id - tmp.minn[]);
else cmax(ans, a[j].id - tmp.minn[]); for (j = i; j <= n && a[j].x == a[j + ].x; ++j) {
modify(a[j].y, a[j].id, a[j].z, , tot, );
}
modify(a[j].y, a[j].id, a[j].z, , tot, );
}
printf("%d\n", ans);
}

Luogu3590 TRZ

Luogu3591[POI2015]ODW

传送门~~

Luogu3597[POI2015]WYC

传送门~~

Luogu3587[POI2015]POD

不出意外又去看题解了, 是比较套路和有思维的题目

大佬写的题解简单易懂: 传送门   (https://www.cnblogs.com/five20/p/9581552.html) byfive20

环形前缀和, 用hash记录每种颜色出现的次数, 在某种颜色的最后一个位置删去该颜色的全部贡献。

两个hash值相同的位置就是可以分割的点。 因为要$r-l$ 尽可能接近mid ,能用单调队列来维护。

Code

 #include<cstdio>
#include<algorithm>
#include<cstring>
#define rd read()
#define ll long long
#define R register
using namespace std; const int N = , base1 = , base2 = , mod1 = 1e9 + , mod2 = 1e9 + ; ll po1[N], po2[N], sum1, sum2;
int last[N], cnt[N], n, k, a[N]; struct node {
int id;
ll s1, s2;
}b[N]; inline int read() {
int X = , p = ; char c = getchar();
for (; c > '' || c < ''; c = getchar())
if (c == '-') p = -;
for (; c >= '' && c <= ''; c = getchar())
X = X * + c - '';
return X * p;
} int cmp(const node &A, const node &B) {
if (A.s1 != B.s1) return A.s1 < B.s1;
if (A.s2 != B.s2) return A.s2 < B.s2;
return A.id < B.id;
} int jud(int x, int y) {
if (b[x].s1 != b[y].s1) return ;
if (b[x].s2 != b[y].s2) return ;
return ;
} void cmin(int &A, int B) {
if (A > B) A = B;
} int Abs(int A) {
return A > ? A : -A;
} int main()
{
n = rd; k = rd;
for (R int i = ; i <= n; ++i)
a[i] = rd;
po1[] = po2[] = ;
for (R int i = ; i <= k; ++i)
po1[i] = po1[i - ] * base1 % mod1,
po2[i] = po2[i - ] * base2 % mod2;
for (R int i = ; i <= n; ++i)
cnt[a[i]]++, last[a[i]] = i;
for (R int i = ; i <= n; ++i) {
(sum1 += po1[a[i]]) %= mod1;
(sum2 += po2[a[i]]) %= mod2;
if (i == last[a[i]])
sum1 = (sum1 - po1[a[i]] * cnt[a[i]] % mod1) % mod1,
sum1 = (sum1 + mod1) % mod1,
sum2 = (sum2 - po2[a[i]] * cnt[a[i]] % mod2) % mod2,
sum2 = (sum2 + mod2) % mod2;
b[i].id = i,
b[i].s1 = sum1,
b[i].s2 = sum2;
}
sort(b + , b + + n, cmp);
ll ans1 = ; int ans2 = n;
int mid = (n + ) >> ;
for (int i = , j = ; i <= n; i = j) {
while (j <= n && jud(i, j)) j++;
ans1 += 1LL * (j - i) * (j - i - ) / ;
for (int l = i, r = i; r < j; ++r) {
while (l < r && b[r].id - b[l].id >= mid) l++;
cmin(ans2, Abs(n - * (b[r].id - b[l].id)));
if (l != i)
cmin(ans2, Abs(n - * (b[r].id - b[l - ].id)));
}
}
printf("%lld %d\n", ans1, ans2);
}

Luogu3587 POD

Luogu3589[POI2015]KUR

神奇的传送门~

POI2015 解题报告的更多相关文章

  1. CH Round #56 - 国庆节欢乐赛解题报告

    最近CH上的比赛很多,在此会全部写出解题报告,与大家交流一下解题方法与技巧. T1 魔幻森林 描述 Cortana来到了一片魔幻森林,这片森林可以被视作一个N*M的矩阵,矩阵中的每个位置上都长着一棵树 ...

  2. 二模13day1解题报告

    二模13day1解题报告 T1.发射站(station) N个发射站,每个发射站有高度hi,发射信号强度vi,每个发射站的信号只会被左和右第一个比他高的收到.现在求收到信号最强的发射站. 我用了时间复 ...

  3. BZOJ 1051 最受欢迎的牛 解题报告

    题目直接摆在这里! 1051: [HAOI2006]受欢迎的牛 Time Limit: 10 Sec  Memory Limit: 162 MBSubmit: 4438  Solved: 2353[S ...

  4. 习题:codevs 2822 爱在心中 解题报告

    这次的解题报告是有关tarjan算法的一道思维量比较大的题目(真的是原创文章,希望管理员不要再把文章移出首页). 这道题蒟蒻以前做过,但是今天由于要复习tarjan算法,于是就看到codevs分类强联 ...

  5. 习题:codevs 1035 火车停留解题报告

    本蒟蒻又来写解题报告了.这次的题目是codevs 1035 火车停留. 题目大意就是给m个火车的到达时间.停留时间和车载货物的价值,车站有n个车道,而火车停留一次车站就会从车载货物价值中获得1%的利润 ...

  6. 习题: codevs 2492 上帝造题的七分钟2 解题报告

    这道题是受到大犇MagHSK的启发我才得以想出来的,蒟蒻觉得自己的代码跟MagHSK大犇的代码完全比不上,所以这里蒟蒻就套用了MagHSK大犇的代码(大家可以关注下我的博客,友情链接就是大犇MagHS ...

  7. 习题:codevs 1519 过路费 解题报告

    今天拿了这道题目练练手,感觉自己代码能力又增强了不少: 我的思路跟别人可能不一样. 首先我们很容易就能看出,我们需要的边就是最小生成树算法kruskal算法求出来的边,其余的边都可以删掉,于是就有了这 ...

  8. NOIP2016提高组解题报告

    NOIP2016提高组解题报告 更正:NOIP day1 T2天天爱跑步 解题思路见代码. NOIP2016代码整合

  9. LeetCode 解题报告索引

    最近在准备找工作的算法题,刷刷LeetCode,以下是我的解题报告索引,每一题几乎都有详细的说明,供各位码农参考.根据我自己做的进度持续更新中......                        ...

随机推荐

  1. linux上静态库和动态库的编译和使用(附外部符号错误浅谈)

    主要参考博客gcc创建和使用静态库和动态库 对于熟悉windows的同学,linux上的静态库.a相当于win的.lib,动态库.so相当于win的.dll. 首先简要地解释下这两种函数库的区别,参考 ...

  2. vue源码核心部分

    1.模板编译   初始化时做的:template ==parse()==>ASTtree ==generate()==>render函数  ==> mount(调用dom方法) 每次 ...

  3. Pascal语言(存档)

    数据类型 标准函数 运算符和表达式 输入语句 输出语句 if语句 case语句 for语句 while语句 repeat语句 函数与过程 形参与实参 全局变量与局部变量 数组 字符串 枚举 子界 集合 ...

  4. django 补充和中间件

    配置 from django.conf import settings form组件 from django.forms import Formfrom django.forms import fie ...

  5. 面向对象object与constructor

    什么是constructor属性?它来自哪里?又将会指向何处? 什么是constructor属性? constructor是构造函数属性. 它来自哪里? 其实constructor属性是来自 prot ...

  6. 关于offsetParent

    不知道有多少人知道JavaScript中有offsetParent这么个属性. 关于offsetParent,我最开始见到他,是在<JavaScript高级程序设计(第3版)>第321页, ...

  7. 一步步实现:springbean的生命周期测试代码

    转载. https://blog.csdn.net/baidu_37107022/article/details/76552052 1. 创建实体SpringBean public class Spr ...

  8. 电商中的库存管理实现-mysql与redis

        库存是电商系统的核心环节,如何做到不少卖,不超卖是库存关心的核心业务问题.业务量大时带来的问题是如何更快速的处理库存计算. 此处以最简模式来讨论库存设计. 以下内容只做分析,不能直接套用,欢迎 ...

  9. Webform(条件查询)

    <html xmlns="http://www.w3.org/1999/xhtml"> <head runat="server"> &l ...

  10. http 文件上传协议图览