「省选测试」拾壹
作者:互联网
划分序列
部分分很好打,给得也很足
很显然是二分答案,关键在于去怎么 \(check\)
-
如果只有正数,可以求出权值和 \(\leq x\) 的最小段数
-
如果只有负数,可以求出权值和 \(\leq x\) 的最大段数
考虑既有正数又有负数的情况,会发现我们既可以求出权值和 \(\leq x\) 的最小段数,也可以求出权值和 \(\leq x\) 的最大段数,不妨考虑直接 \(dp\)
定义 \(f[n]\) 表示 \(1\sim n\) 可以划分出的最大段数,\(g[n]\) 表示 \(1\sim n\) 可以划分出的最小段数
考虑转移
\[f[i]=\max\{[sum[i]-sum[j - 1] \leq x]f[j]\}+1 \]\[g[i]=\max\{[sum[i]-sum[j - 1] \leq x]g[j]\}+1 \]但是这样直接做是 \(\Theta (n^2)\) 的,对于中间的那个条件,可以考虑离散化一下,然后用权值线段树或者是树状数组去优化一下,从而做到单次 \(check\) 是 \(n\log n\) 的
代码
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
const int maxn = 1e5 + 50, INF = 0x3f3f3f3f;
inline int read () {
register int x = 0, w = 1;
register char ch = getchar ();
for (; ch < '0' || ch > '9'; ch = getchar ()) if (ch == '-') w = -1;
for (; ch >= '0' && ch <= '9'; ch = getchar ()) x = x * 10 + ch - '0';
return x * w;
}
int n, m, len, L = - INF, R = INF;
int a[maxn], b[maxn], sum[maxn], res[maxn], tmp[maxn];
int f[maxn], g[maxn];
inline void Insert1 (register int x, register int val) {
for (; x; x -= x & -x) f[x] = max (f[x], val);
}
inline int Query1 (register int x, register int ans = -INF) {
for (; x <= maxn; x += x & -x) ans = max (ans, f[x]);
return ans;
}
inline void Insert2 (register int x, register int val) {
for (; x; x -= x & -x) g[x] = min (g[x], val);
}
inline int Query2 (register int x, register int ans = INF) {
for (; x <= maxn; x += x & -x) ans = min (ans, g[x]);
return ans;
}
inline bool Check (register int x, register int ans1 = 0, register int ans2 = 0) {
b[len = 1] = x, memset (f, - 0x3f, sizeof f), memset (g, 0x3f, sizeof g);
for (register int i = 1; i <= n; i ++) b[++ len] = sum[i], b[++ len] = x + sum[i];
sort (b + 1, b + len + 1), len = unique (b + 1, b + len + 1) - b - 1;
for (register int i = 1; i <= n; i ++) res[i] = lower_bound (b + 1, b + len + 1, sum[i]) - b, tmp[i] = lower_bound (b + 1, b + len + 1, x + sum[i]) - b;
x = lower_bound (b + 1, b + len + 1, x) - b, Insert1 (x, 0), Insert2 (x, 0);
for (register int i = 1; i <= n; i ++) ans1 = Query1 (res[i]) + 1, Insert1 (tmp[i], ans1);
for (register int i = 1; i <= n; i ++) ans2 = Query2 (res[i]) + 1, Insert2 (tmp[i], ans2);
return ans2 <= m && m <= ans1;
}
int main () {
n = read(), m = read();
for (register int i = 1; i <= n; i ++) a[i] = read(), sum[i] = sum[i - 1] + a[i];
while (L <= R) {
register int mid = (L + R) >> 1;
if (Check (mid)) R = mid - 1;
else L = mid + 1;
}
printf ("%d\n", L);
return 0;
}
Ring
对于每个左端点 \(l\) 我们可以求出来最小的符合条件的 \(r\),这样可以做到用双指针 \(\Theta (n)\) 去扫
在判断是否符合条件的时候用 \(LCT\) 去维护一下就好啦
代码
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
const int maxn = 3e5 + 50, INF = 0x3f3f3f3f;
inline int read () {
register int x = 0, w = 1;
register char ch = getchar ();
for (; ch < '0' || ch > '9'; ch = getchar ()) if (ch == '-') w = -1;
for (; ch >= '0' && ch <= '9'; ch = getchar ()) x = x * 10 + ch - '0';
return x * w;
}
int n, m, q, R[maxn];
struct Edge {
int from, to;
} e[maxn];
struct Tree {
int fa, ch[2], lazy;
} tree[maxn];
inline bool Isroot (register int rt) {
return tree[tree[rt].fa].ch[0] != rt && tree[tree[rt].fa].ch[1] != rt;
}
inline void Pushdown (register int rt) {
if (! tree[rt].lazy) return;
tree[tree[rt].ch[0]].lazy ^= 1, tree[tree[rt].ch[1]].lazy ^= 1;
tree[rt].lazy = 0, swap (tree[rt].ch[0], tree[rt].ch[1]);
}
inline void Recall (register int rt) {
if (! Isroot (rt)) Recall (tree[rt].fa);
Pushdown (rt);
}
inline void Rotate (register int x) {
register int y = tree[x].fa, z = tree[y].fa, k = tree[y].ch[1] == x;
if (! Isroot (y)) tree[z].ch[tree[z].ch[1] == y] = x; tree[x].fa = z;
tree[y].ch[k] = tree[x].ch[! k], tree[tree[x].ch[! k]].fa = y;
tree[x].ch[! k] = y, tree[y].fa = x;
}
inline void Splay (register int x) {
Recall (x);
while (! Isroot (x)) {
register int y = tree[x].fa, z = tree[y].fa;
if (! Isroot (y)) (tree[y].ch[0] == x) == (tree[z].ch[0] == y) ? Rotate (y) : Rotate (x);
Rotate (x);
}
}
inline void Access (register int x, register int y = 0) {
for (; x; y = x, x = tree[x].fa) Splay (x), tree[x].ch[1] = y;
}
inline void Makeroot (register int x) {
Access (x), Splay (x), tree[x].lazy ^= 1;
}
inline int Findroot (register int x) {
Access (x), Splay (x);
while (tree[x].ch[0]) x = tree[x].ch[0];
return x;
}
inline void Split (register int x, register int y) {
Makeroot (x), Access (y), Splay (y);
}
inline void Link (register int x, register int y) {
Makeroot (x);
if (Findroot (y) != x) tree[x].fa = y;
}
inline void Cut (register int x, register int y) {
if (Findroot (x) != Findroot (y)) return;
Split (x, y);
if (tree[y].ch[0] == x && tree[x].ch[1] == 0) tree[y].ch[0] = tree[x].fa = 0;
}
int main () {
n = read(), m = read(), q = read();
for (register int i = 1; i <= m; i ++) e[i].from = read(), e[i].to = read();
register int r = 1;
for (register int l = 1; l <= m; l ++) {
while (r <= m) {
register int u = e[r].from, v = e[r].to;
if (Findroot (u) == Findroot (v)) break;
else Link (u, v), r ++;
}
R[l] = r, Cut (e[l].from, e[l].to);
}
while (q --) {
register int l = read(), r = read();
if (r >= R[l]) puts (">_<");
else puts ("QAQ");
}
return 0;
}
Match
对于最后答案,打表(能打出来???)或者推式子(我还不会),得到这样一个通项公式
然后可以带进去直接做
代码
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
const int maxn = 1e5 + 50, INF = 0x3f3f3f3f, mod = 1e9 + 7;
inline int read () {
register int x = 0, w = 1;
register char ch = getchar ();
for (; ch < '0' || ch > '9'; ch = getchar ()) if (ch == '-') w = -1;
for (; ch >= '0' && ch <= '9'; ch = getchar ()) x = x * 10 + ch - '0';
return x * w;
}
inline int qpow (register int a, register int b, register int ans = 1) {
for (; b; b >>= 1, a = 1ll * a * a % mod) if (b & 1) ans = 1ll * ans * a % mod;
return ans;
}
int n;
int main () {
n = read();
printf ("%lld\n", 1ll * ((10ll * n * n % mod - 13ll * n % mod - 1 + 4ll * qpow (n, mod - 2) % mod) % mod + mod) % mod * qpow (45, mod - 2) % mod);
return 0;
}
标签:拾壹,ch,leq,省选,段数,int,测试,include,mod 来源: https://www.cnblogs.com/Rubyonly233/p/14341593.html