由于博主没有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. easyui layout布局的属性说明

    layout布局的属性说明: 名称 类型 描述 默认值 fit boolean 当设置为 true 时,就设置布局(layout)的尺寸适应它的父容器.当在 'body' 标签上创建布局(layout ...

  2. windows time-wait 问题处理记录

    问题描述:有一段时间,服务器启动了好多程序,做的是 obd监听服务,连接好多个服务器,由于程序的本身的问题造成大量的wait-time,一番百度后找到找到方案1 设置一由于wait-time 需要经过 ...

  3. Hive环境的安装

    hive是什么:hive是基于Hadoop的一个数据仓库工具,可以将结构化的数据文件映射为一张数据库表,并提供简单的sql查询功能(HQL) hive有什么用 1.通过类SQL语句快速实现简单的Map ...

  4. VMware Workstation 11 搭建windows server 2012 之sql server 2012集群常见问题整理

    1.windows server 2012内置支持iSCSI发起程序无需额外安装,iSCSI Software Target 可作为“文件和存储服务”角色下的内置功能使用 2.拷贝虚拟机的文件加入域时 ...

  5. centos 设置中文环境

    方法1: [hl@localhost ~]$ LANG=zh_CN.UTF-8 #只对当前shell有效,临时设置 [hl@localhost ~]$ ll 总用量 drwxrwxr-x. hl hl ...

  6. spring boot 常见三十四问

    Spring Boot 是微服务中最好的 Java 框架. 我们建议你能够成为一名 Spring Boot 的专家. 问题一 Spring Boot.Spring MVC 和 Spring 有什么区别 ...

  7. nginx+python+windows 开始_02

    接上文:http://www.cnblogs.com/tacyeh/p/4790112.html 一.改造helloWorld.py import web urls = ('/', 'Home', ' ...

  8. leetcode5

    public class Solution { private int lo, maxLen; public String LongestPalindrome(String s) { int len ...

  9. __getitem__ __setitem__ __delitem__ 使用

    #__getitem__ __setitem__ __delitem__运行设置key value值了class fun: def __init__(self): print('test') def ...

  10. 记号一次更换IBM X3650M4主板后RAID无法启动的解决

    https://wenku.baidu.com/view/9d503ef367ec102de2bd89d7.html 强烈感谢上面分享文档的大侠!! 1.更换主板后,linux系统,无法加载引导.需要 ...