NOIP第二阶段总结
要不是龙爹放水我就第9了,都好强呀QwQ
noip晚间小测5
阶段排名 8
A 统计
-
数据太水,最后10分钟改了个n2就过了2e5
-
f[i]为以i为开头的逆序对个数,发现每次排序都会将这些点的f值减去,而且每个只能减去一次,所以找到第一次减是什么时候,树状数组维护即可
Show Code
#include <cstdio>
#include <cstring>
#include <algorithm>
const int N = 2e5 + 5;
int read(int x = 0, int f = 1, char c = getchar()) {
for (; c < '0' || c > '9'; c = getchar())
if (c == '-') f = -1;
for (; c >= '0' && c <= '9'; c = getchar())
x = x * 10 + c - '0';
return x * f;
}
int n, m, a[N], t[N], f[N], b[N];
long long ans, d[N];
void Add(int x, int w) {
for (; x; x -= x & -x)
t[x] = std::min(t[x], w);
}
int Ask(int x, int ans = 1e9) {
for (; x <= n; x += x & -x)
ans = std::min(ans, t[x]);
return ans;
}
int main() {
freopen("count.in", "r", stdin);
freopen("count.out", "w", stdout);
n = read(); m = read();
for (int i = 1; i <= n; ++i) a[i] = read();
for (int i = n; i >= 1; --i) {
for (int x = a[i] - 1; x; x -= x & -x) f[i] += t[x];
for (int x = a[i]; x <= n; x += x & -x) t[x]++;
ans += f[i];
}
printf("%lld ", ans);
for (int i = 1; i <= n; ++i) t[i] = m + 1;
for (int i = 1; i <= m; ++i) {
int x = read();
if (!b[x]) b[x] = i;
}
for (int i = 1; i <= n; ++i) {
if (b[i]) Add(a[i], b[i]);
d[Ask(a[i])] += f[i];
}
for (int i = 1; i <= m; ++i)
printf("%lld ", ans -= d[i]);
return 0;
}
B 点亮 (Unaccepted)
- 暴力写挂了,成0分了
Show Code
模拟赛40
阶段排名 8
今天这4道题都挺简单,考场上就T2没想出来,可是其他的也没过
A 进化
-
题意不太好理解,考完被卡了
-
lao星左面出现不了t,所以t开头的就不是lao星的
-
l旁边无论怎么边都会是t,如果有若干的l连起来就不是这两个星的
-
还有一种我没考虑到,就是没有l的肯定不是这两个星的
Show Code
#include <cstdio>
#include <cstring>
const int N = 1e7 + 5;
char a[N];
int main() {
freopen("laotui.in", "r", stdin);
freopen("laotui.out", "w", stdout);
int T; scanf("%d", &T);
while (T--) {
scanf("%s", a + 1);
int n = strlen(a + 1), l = 1, t = 1, g = 0;
if (a[1] == 't') l = 0;
for (int i = 1; i <= n && (l + t); ++i)
if ((a[i] != 'l' && a[i] != 't') || ((a[i] == 'l' && (g = 1)) && i + 1 <= n && a[i+1] == 'l')) l = t = 0;
if (!g) l = t = 0;
printf("%d %d\n", l, t);
}
return 0;
}
B 皇室战争
-
考场rand了70分,lemon上80分
-
正解是先n3DP处理出一段区间是否可以全部合并,然后n2Dp一遍求出到i的最大答案
Show Code
#include <cstdio>
#include <algorithm>
typedef long long ll;
const int N = 805;
int read(int x = 0, int f = 1, char c = getchar()) {
for (; c < '0' || c > '9'; c = getchar())
if (c == '-') f = -1;
for (; c >='0' && c <='9'; c = getchar())
x = x * 10 + c - '0';
return x * f;
}
int n, k, a[N];
bool g[N][N];
ll s[N], f[N];
int main() {
freopen("clash.in", "r", stdin);
freopen("clash.out", "w", stdout);
n = read(); k = read();
for (int i = 1; i <= n; ++i)
a[i] = read(), s[i] = s[i-1] + read();
for (int i = n - 1; i >= 1; --i) {
if (a[i] + a[i+1] <= k) g[i][i+1] = 1;
for (int j = i + 3; j <= n; j += 2) {
g[i][j] = ((a[i] + a[j] <= k) && (g[i+1][j-1]));
for (int l = i + 1; l < j && !g[i][j]; ++l)
g[i][j] = (g[i][l] && g[l+1][j]);
}
}
for (int i = 1; i <= n; ++i, f[i] = f[i-1])
for (int j = i - 1; j >= 0; j--)
f[i] = std::max(f[i], f[j] + (g[j+1][i] ? s[i] - s[j] : 0ll));
printf("%lld\n", f[n]);
return 0;
}
C MC
-
显然时光回溯
-
考场上写了个假的线段树合并,不过按大小优化了一下
-
没考虑一个边被删了多次的情况,这样的话应该在第一次删的时候合并,被卡了30分(std也写错了,后来改对之后就把我卡了)
-
最坑人的是还有智商为0的生物,题目中说星球原来的生物的智商是正整数,新加的时候没说,我就以为没有这种情况,15分没了(我感觉我就是这种生物)
Show Code
#include <cstdio>
#include <algorithm>
#define ls t[rt].l
#define rs t[rt].r
#define lx t[x].l
#define rx t[x].r
const int N = 1e5 + 5, M = 19260817;
int read(int x = 0, int f = 1, char c = getchar()) {
for (; c < '0' || c > '9'; c = getchar())
if (c == '-') f = -1;
for (; c >= '0' && c <= '9'; c = getchar())
x = x * 10 + c - '0';
return x * f;
}
struct Node {
int x, y;
}e[N];
int n, m, q, f[N], ans[N], v[N];
int od[N], a[N], s[N], z[N];
int fac[N*20], fai[N*20];
int Find(int x) {
return x == f[x] ? x : (f[x] = Find(f[x]));
}
int Pow(int a, int k, int ans = 1) {
for (; k; k >>= 1, a = 1ll * a * a % M)
if (k & 1) ans = 1ll * ans * a % M;
return ans;
}
void Pre(int n) {
fac[0] = 1;
for (int i = 1; i <= n; ++i)
fac[i] = 1ll * fac[i-1] * i % M;
fai[n] = Pow(fac[n], M - 2);
for (int i = n; i >= 1; --i)
fai[i-1] = 1ll * fai[i] * i % M;
}
struct Tree {
int s, l, r;
}t[N*40];
int root[N], trc;
void Change(int &rt, int l, int r, int x, int w) {
if (!rt) rt = ++trc;
t[rt].s += w;
if (l == r) return;
int mid = l + r >> 1;
if (x <= mid) Change(ls, l, mid, x, w);
else Change(rs, mid + 1, r, x, w);
}
void Merge(int &rt, int x, int l, int r) {
if (!rt) return rt = x, void();
t[rt].s += t[x].s;
int mid = l + r >> 1;
if (lx) Merge(ls, lx, l, mid);
if (rx) Merge(rs, rx, mid + 1, r);
}
int Ask(int rt, int l, int r, int x) {
if (l == r) return t[rt].s;
int mid = l + r >> 1;
if (x <= mid) return Ask(ls, l, mid, x);
else return Ask(rs, mid + 1, r, x);
}
int main() {
freopen("mc.in", "r", stdin);
freopen("mc.out", "w", stdout);
int sum = 0; n = read(); m = read(); q = read();
for (int i = 1; i <= n; ++i) {
int k = read(), x = read(); sum += k;
Change(root[i], 0, n, x, k);
}
for (int i = 1; i <= m; ++i)
e[i] = (Node) {read(), read()};
for (int i = 1; i <= q; ++i) {
od[i] = read();
if (od[i] == 1) {
a[i] = read(); s[i] = read(); z[i] = read(); sum += s[i];
if (!z[i]) fputs("No", stderr);
Change(root[a[i]], 0, n, z[i], s[i]);
}
else if (od[i] == 2) a[i] = read(), v[a[i]]++;
else a[i] = read(), s[i] = read(), z[i] = read();
}
Pre(sum);
for (int i = 1; i <= n; ++i) f[i] = i;
for (int i = 1; i <= m; ++i) {
if (v[i]) continue;
int x = Find(e[i].x), y = Find(e[i].y);
if (x == y) continue;
if (t[root[x]].s < t[root[y]].s) std::swap(x, y);
f[y] = x; Merge(root[x], root[y], 0, n);
}
for (int i = q; i >= 1; --i) {
if (od[i] == 1) Change(root[Find(a[i])], 0, n, z[i], -s[i]);
else if (od[i] == 2) {
if (--v[a[i]]) continue;
int x = Find(e[a[i]].x), y = Find(e[a[i]].y);
if (x == y) continue;
if (t[root[x]].s < t[root[y]].s) std::swap(x, y);
f[y] = x; Merge(root[x], root[y], 0, n);
}
else {
int x = Find(a[i]), y = Ask(root[x], 0, n, z[i]);
sum = t[root[x]].s;
if (y < s[i]) ans[i] = 0;
else ans[i] = 1ll * fac[y] * fac[sum-s[i]] % M * fai[y-s[i]] % M * fai[sum] % M;
}
}
for (int i = 1; i <= q; ++i)
if (od[i] == 3) printf("%d\n", ans[i]);
return 0;
}
D 简单题
- 最小生成树后树剖线段树,考场上就剩半小时,就没写出来,有好多细节没注意到
Show Code
#include <cstdio>
#include <algorithm>
#define ls (rt << 1)
#define rs (rt << 1 | 1)
#define Max(a, b) ({int xx = a, yy = b; xx > yy ? xx : yy;})
#define Min(a, b) ({int xx = a, yy = b; xx < yy ? xx : yy;})
const int N = 1e5 + 5;
int read(int x = 0, int f = 1, char c = getchar()) {
for (; c < '0' || c > '9'; c = getchar())
if (c == '-') f = -1;
for (; c >='0' && c <='9'; c = getchar())
x = x * 10 + c - '0';
return x * f;
}
struct Edge {
int next, t, d, id;
}e[N<<1];
int head[N], edc;
void Add(int x, int y, int z, int i) {
e[++edc] = (Edge) {head[x], y, z, i}; head[x] = edc;
e[++edc] = (Edge) {head[y], x, z, i}; head[y] = edc;
}
struct Node {
int x, y, d, id;
bool operator < (const Node &b) const {
return d < b.d;
}
}a[N*10];
int n, m, t[N<<2], tag[N<<2], ans[N*10], f[N];
int fa[N], dep[N], son[N], siz[N], c[N];
int tp[N], dfn[N], dfc, id[N];
bool g[N*10];
int Find(int x) {
return x == f[x] ? x : f[x] = Find(f[x]);
}
void Dfs(int x) {
siz[x] = 1; dep[x] = dep[fa[x]] + 1;
for (int i = head[x], y; i; i = e[i].next) {
if ((y = e[i].t) == fa[x]) continue;
c[y] = e[i].id; fa[y] = x;
Dfs(y); siz[x] += siz[y];
if (!son[x] || siz[y] > siz[son[x]]) son[x] = y;
}
}
void Dfs(int x, int top) {
tp[x] = top; dfn[x] = ++dfc; id[dfc] = x;
if (!son[x]) return;
Dfs(son[x], top);
for (int i = head[x], y; i; i = e[i].next)
if ((y = e[i].t) != fa[x] && y != son[x]) Dfs(y, y);
}
void Build(int rt, int l, int r) {
tag[rt] = 1e9;
if (l == r) return t[rt] = a[c[id[l]]].d, void();
int mid = l + r >> 1;
Build(ls, l, mid); Build(rs, mid + 1, r);
t[rt] = Max(t[ls], t[rs]);
}
int Ask(int rt, int l, int r, int x, int y, int w) {
if (x <= l && r <= y) return tag[rt] = Min(tag[rt], w), t[rt];
int mid = l + r >> 1, ans = 0;
if (x <= mid) ans = Ask(ls, l, mid, x, y, w);
if (y > mid) ans = Max(ans, Ask(rs, mid + 1, r, x, y, w));
return ans;
}
void Dfs(int rt, int l, int r, int s) {
s = Min(s, tag[rt]);
if (l == r) return ans[a[c[id[l]]].id] = s, void();
int mid = l + r >> 1;
Dfs(ls, l, mid, s); Dfs(rs, mid + 1, r, s);
}
int main() {
freopen("easy.in", "r", stdin);
freopen("easy.out", "w", stdout);
n = read(); m = read();
for (int i = 1; i <= m; ++i)
a[i] = (Node) {read(), read(), read(), i};
std::sort(a + 1, a + m + 1);
for (int i = 1; i <= n; ++i) f[i] = i;
for (int i = 1; i <= m; ++i) {
int x = Find(a[i].x), y = Find(a[i].y);
if (x == y) continue;
f[y] = x; g[i] = 1;
Add(a[i].x, a[i].y, a[i].d, i);
}
Dfs(1); Dfs(1, 1); Build(1, 2, n);
for (int i = 1; i <= m; ++i) {
if (g[i]) continue;
int &mx = ans[a[i].id];
int x = a[i].x, y = a[i].y, d = a[i].d;
while (tp[x] != tp[y]) {
if (dep[tp[x]] < dep[tp[y]]) std::swap(x, y);
mx = Max(mx, Ask(1, 2, n, dfn[tp[x]], dfn[x], d));
x = fa[tp[x]];
}
if (dfn[x] > dfn[y]) std::swap(x, y);
if (x != y) mx = Max(mx, Ask(1, 2, n, dfn[x] + 1, dfn[y], d));
}
Dfs(1, 2, n, 1e9);
for (int i = 1; i <= m; ++i)
printf("%d\n", ans[i]);
return 0;
}
noip晚间小测4
阶段排名 6
A 次芝麻
- 这个阶段的原题……
Show Code
#include <cstdio>
#include <algorithm>
int Pow(int a, int k, int M, int ans = 1) {
for (; k; k >>= 1, a = 1LL * a * a % M)
if (k & 1) ans = 1LL * ans * a % M;
return ans;
}
int main() {
freopen("sesame.in", "r", stdin);
freopen("sesame.out", "w", stdout);
int n, m, k;
scanf("%d%d%d", &n, &m, &k);
k = 1LL * n * Pow(2, k, n + m) % (n + m);
printf("%d\n", std::min(k, n + m - k));
return 0;
}
B 喝喝喝
- 根号筛因数,双指针扫一遍就行了
Show Code
#include <cstdio>
const int N = 1e5 + 5;
int read(int x = 0, int f = 1, char c = getchar()) {
for (; c < '0' || c > '9'; c = getchar())
if (c == '-') f = -1;
for (; c >= '0' && c <= '9'; c = getchar())
x = x * 10 + c - '0';
return x * f;
}
int n, m, a[N], v[N];
long long ans;
int main() {
freopen("drink.in", "r", stdin);
freopen("drink.out", "w", stdout);
n = read(); m = read();
for (int i = 1; i <= n; ++i) a[i] = read();
for (int i = 1, j = 1; i <= n; ++i) {
for (; j <= n && ((!v[a[j]] && !v[0]) || a[j] <= m); ++j) {
int x = a[j] - m;
if (!x) v[x]++;
for (int k = 1; k * k <= x; ++k)
if (x % k == 0) v[k]++, v[x/k]++;
}
int x = a[i] - m;
if (!x) v[x]--;
for (int k = 1; k * k <= x; ++k)
if (x % k == 0) v[k]--, v[x/k]--;
ans += j - i;
}
printf("%lld\n", ans);
return 0;
}
C 长寿花 (Unaccepted)
- 感觉像是神奇DP就放弃了
Show Code
模拟赛39
阶段排名 10
A 最大K段和
-
以前老姚讲过,可记不清了,考场上写了个假的贪心但数据太水就A了(直接输出所有正数的和就能A)
-
可以用线段树维护区间最大子段和,但是好像不是很好写,就没写
-
还可以用可撤销贪心
-
将所有相邻的符号相同的都和成一个数,然后将子段个数缩小,可以减去一个正数,也可以把负数加上将相邻的两个合并
-
实现就是正数取反,然后像种树那样做就好了
Show Code
#include <bits/stdc++.h>
typedef long long ll;
const int N = 1e5 + 5;
int read(int x = 0, int f = 1, char c = getchar()) {
for (; c < '0' || c > '9'; c = getchar())
if (c == '-') f = -1;
for (; c >= '0' && c <= '9'; c = getchar())
x = x * 10 + c - '0';
return x * f;
}
std::priority_queue< std::pair<ll, int> > q;
int n, m, l[N], r[N];
ll a[N], ans;
bool v[N];
int main() {
freopen("maxksum.in", "r", stdin);
freopen("maxksum.out", "w", stdout);
n = read(), m = read();
int cnt = 0;
for (int i = 1; i <= n; ++i) {
int x; scanf("%d", &x);
if (!cnt && x <= 0) continue;
if (!cnt) a[cnt = 1] = x;
else if (((x > 0) ^ (a[cnt] > 0)) && x) a[++cnt] = x;
else a[cnt] += x;
}
n = cnt;
if (a[n] <= 0) n--;
for (int i = 1; i <= n; ++i) {
l[i] = i - 1, r[i] = i + 1;
if (i & 1) q.push(std::make_pair(a[i] = -a[i], i)), ans -= a[i];
else q.push(std::make_pair(a[i], i));
}
l[1] = r[n] = 0;
for (m = n / 2 + 1 - m; m > 0 && !q.empty(); m--) {
while (!q.empty() && v[q.top().second]) q.pop();
if (q.empty()) break;
ans += q.top().first;
int x = q.top().second; q.pop();
if (!l[x]) v[r[x]] = 1, l[r[r[x]]] = 0;
if (!r[x]) v[l[x]] = 1, r[l[l[x]]] = 0;
if (l[x] && r[x]) {
v[l[x]] = v[r[x]] = 1;
q.push(std::make_pair(a[x] = a[l[x]] + a[r[x]] - a[x], x));
l[x] = l[l[x]]; r[l[x]] = x;
r[x] = r[r[x]]; l[r[x]] = x;
}
}
printf("%lld\n", ans);
return 0;
}
B 双端队列xLIS问题
-
考场上把倒着模拟了一下,发现了个规律,写的特麻烦
-
正解就是把序列倒过来放在前面,然后最长上升子序列就是答案
-
构造其实挺显然的吧
Show Code
#include <cstdio>
#include <algorithm>
#define Max(x, y) (x > y ? x : y)
const int N = 1e5 + 5;
int read(int x = 0, int f = 1, char c = getchar()) {
for (; c < '0' || c > '9'; c = getchar())
if (c == '-') f = -1;
for (; c >= '0' && c <= '9'; c = getchar())
x = x * 10 + c - '0';
return x * f;
}
int n, m, a[N*2], b[N], ans, t[N];
void Add(int x, int w) {
for (; x <= m; x += x & -x)
t[x] = Max(t[x], w);
}
int Ask(int x, int ans = 0) {
for (; x; x -= x & -x)
ans = Max(ans, t[x]);
return ans;
}
int main() {
freopen("dequexlis.in", "r", stdin);
freopen("dequexlis.out", "w", stdout);
n = read();
for (int i = n; i >= 1; --i)
a[i] = b[i] = read();
std::sort(b + 1, b + n + 1);
m = std::unique(b + 1, b + n + 1) - b - 1;
for (int i = 1; i <= n; ++i)
a[i] = a[n+n-i+1]= std::lower_bound(b + 1, b + m + 1, a[i]) - b;
for (int i = 1, x; i <= n * 2; ++i) {
Add(a[i], x = Ask(a[i] - 1) + 1);
ans = Max(ans, x);
}
printf("%d\n", ans);
return 0;
}
C 最大前缀和
- 类似卡特兰数的推法可以求出最大前缀和小于x的方案数,容斥一下即可求出答案
Show Code
#include <cstdio>
const int N = 4005, M = 998244853;
int n, m, fac[N], fai[N], ans;
int Pow(int a, int k, int ans = 1) {
for (; k; k >>= 1, a = 1LL * a * a % M)
if (k & 1) ans = 1LL * ans * a % M;
return ans;
}
int C(int n, int m) {
if (n < 0 || m < 0 || n < m) return 0;
return 1LL * fac[n] * fai[m] % M * fai[n-m] % M;
}
void Init(int n) {
fac[0] = 1;
for (int i = 1; i <= n; ++i)
fac[i] = 1LL * fac[i-1] * i % M;
fai[n] = Pow(fac[n], M - 2);
for (int i = n; i >= 1; --i)
fai[i-1] = 1LL * fai[i] * i % M;
}
int main() {
freopen("maxpsum.in", "r", stdin);
freopen("maxpsum.out", "w", stdout);
scanf("%d%d", &n, &m);
Init(n + m);
for (int i = n; i >= 0 && i >= n - m; --i)
if ((ans += 1LL * i * (C(n + m, n - i) - C(n + m, n - i - 1) + M) % M) >= M) ans -= M;
printf("%d\n", ans);
return 0;
}
D 积木 (Unaccpeted)
- 概率题,溜了溜了
Show Code
noip晚间小测3
阶段排名 9
A 艰难睡眠
- 就是求固定区间长度的最小值,单调队列即可,写丑了,题库上TLE成80
Show Code
#include <cstdio>
#define getchar() (iS == iT ? (iT = (iS = ibuf) + fread(ibuf, 1, L, stdin), (iS == iT ? EOF : *iS++)) : *iS++)
const int L = (1 << 21) + 1; char ibuf[L], *iS, *iT;
const int N = 5005, M = 2005;
int read(int x = 0, int f = 1, char c = getchar()) {
for (; c < '0' && c <= '0'; c = getchar())
if (c == '-') f = -1;
for (; c >= '0' && c <= '9'; c = getchar())
x = x * 10 + c - '0';
return x * f;
}
int n, m, k, a[M*2], d[N], q[M*2], ans = 1e9, f[M];
int main() {
freopen("sleep.in", "r", stdin);
freopen("sleep.out", "w", stdout);
n = read(); m = read(); k = m - read();
for (int i = 1; i <= n; ++i) {
read(), d[i] = k - read() + 1;
if (d[i] <= 0) return puts("-1"), 0;
}
for (int i = 1; i <= n; ++i) {
for (int j = 1; j <= m; ++j)
a[j] = a[j+m] = read();
int l = 1, r = 0;
for (int j = 1; j < m + d[i]; ++j) {
while (l <= r && q[l] + d[i] <= j) l++;
while (l <= r && a[q[r]] >= a[j]) r--;
q[++r] = j;
if (j >= d[i]) f[j-d[i]+1] += a[q[l]];
}
}
for (int i = 1; i <= m; ++i)
if (f[i] < ans) ans = f[i];
printf("%d\n", ans);
return 0;
}
B 路径难题 (Unaccepted)
- Dij50分
Show Code
模拟赛38
阶段排名 12
A 理发
- 考虑一层一层放头发,最多只能放到原来的高度,然后树状数组求出前面有几个放到原来高度的,剩下的就是新增的逆序对数
Show Code
#include <cstdio>
#include <vector>
const int N = 1e5 + 5;
int read(int x = 0, int f = 1, char c = getchar()) {
for (; c < '0' || c > '9'; c = getchar())
if (c == '-') f = -1;
for (; c >= '0' && c <= '9'; c = getchar())
x = x * 10 + c - '0';
return x * f;
}
int n, a[N], t[N];
long long ans;
std::vector<int> p[N];
void Add(int x) {
for (; x <= n; x += x & -x) t[x]++;
}
int Ask(int x, int ans = 0) {
for (; x; x -= x & -x) ans += t[x];
return ans;
}
int main() {
freopen("haircut.in", "r", stdin);
freopen("haircut.out", "w", stdout);
n = read();
for (int i = 1; i <= n; ++i)
p[a[i] = read()].push_back(i);
for (int i = 0; i < n; ++i) {
printf("%lld\n", ans);
for (int j = 0; j < p[i].size(); ++j)
Add(p[i][j]), ans += p[i][j] - Ask(p[i][j]);
}
return 0;
}
B T形覆盖
-
特殊点向四周建边
-
并查集维护联通块内特殊点的个数sb,非特殊点的个数sw,特殊点与特殊点间边的个数se,非特殊点边权最小值mi
-
对于每个联通块:
sw==1
:没有与特殊点连边se==1或0,sw==3sb
:一个特殊点对应三个非特殊点se==0,sw==3sb+1
:多一个非特殊点,把点权最小的扔掉即可- 其他情况都是会重叠的,输出No
Show Code
#include <cstdio>
#include <algorithm>
const int N = 1e6 + 5;
int read(int x = 0, int f = 1, char c = getchar()) {
for (; c < '0' || c > '9'; c = getchar())
if (c == '-') f = -1;
for (; c >= '0' && c <= '9'; c = getchar())
x = x * 10 + c - '0';
return x * f;
}
int n, m, a[N], f[N], sw[N], sb[N], se[N], p[N], ans;
bool b[N], v[N];
int Find(int x) {
return x == f[x] ? x : (f[x] = Find(f[x]));
}
int Get(int x, int od) {
if (od == 0) return x / m ? x - m : -1;//up
if (od == 1) return x / m != n - 1 ? x + m : -1;//down
if (od == 2) return x % m != 0 ? x - 1 : -1;//left
if (od == 3) return x % m != m - 1 ? x + 1 : -1;//right
}
int main() {
freopen("t_covering.in", "r", stdin);
freopen("t_covering.out", "w", stdout);
n = read(); m = read();
for (int i = 0; i < n * m; ++i)
a[i] = read(), f[i] = i, sw[i] = 1;
int k = read();
for (int i = 1; i <= k; ++i) {
int x = read() * m + read();
p[i] = x;
b[x] = v[x] = 1;
sw[x] = 0; sb[x] = 1;
ans += a[x]; a[x] = 1e9;
}
for (int i = 1; i <= k; ++i) {
int x = p[i];
for (int j = 0; j < 4; ++j, x = p[i]) {
int y = Get(x, j); x = Find(x);
if (y == -1) continue;
if (b[y]) se[x]++;
if (!v[y]) ans += a[y], v[y] = 1;
y = Find(y);
if (x == y) continue;
f[y] = x;
sw[x] += sw[y]; sb[x] += sb[y]; se[x] += se[y];
a[x] = std::min(a[x], a[y]);
}
}
for (int i = 0; i <= n * m; ++i) {
if (Find(i) != i) continue;
if ((se[i] <= 2 && sw[i] == sb[i] * 3) || sw[i] == 1);//判断se==2是因为我一条边算了两遍
else if (!se[i] && sw[i] == sb[i] * 3 + 1) ans -= a[i];
else return puts("No"), 0;
}
printf("%d\n", ans);
return 0;
}
C 迷途竹林 (Unaccepted)
- 打了个前20分的写法,把竹子长m天放堆里,砍km次,每次砍把堆顶砍p再放回去,最后输出堆顶,数据太水,于是它A了
Show Code
D 游戏 (Unaccepted)
- 写了个搜索结果环到了自己,然后就死了
Show Code
联赛模拟测试37
阶段排名 27
A 简单题
-
考场上想出a+b+c是定值且q没啥用,但还是没找到规律
-
观察式子,将A和B加起来就可以把P消掉
-
然后每次变化就是A+B和C两个数中大的减小的,小的变两倍
-
如果C小,C变两倍,
-
如果C大,\(C=C-(A+B)=C-((A+B+C)-C)=2C-(A+B+C)\),C变两倍减去和,和是定值,答案就转换成\(C*2^k\mod (A+B+C)\)
Show Code
#include <cstdio>
int read(int x = 0, int f = 1, char c = getchar()) {
for (; c < '0' || c > '9'; c = getchar())
if (c == '-') f = -1;
for (; c >='0' && c <='9'; c = getchar())
x = x * 10 + c - '0';
return x * f;
}
int Pow(int a, int k, int M, int ans = 1) {
for (; k; k >>= 1, a = 1LL * a * a % M)
if (k & 1) ans = 1LL * ans * a % M;
return ans;
}
int main() {
freopen("easy.in", "r", stdin);
freopen("easy.out", "w", stdout);
int T = read();
while (T--) {
int x = read() + read(), y = read(), k = read();
x += y;
printf("%lld\n", 1LL * y * Pow(2, k, x) % x);
}
return 0;
}
B 斗地主
-
我记得学长讲过,但忘了怎么写了,本来想写60分二分图,然后懒得写,就写了个40分暴搜,有个地方写挂了就只剩20了
-
每张牌上正面的数向背面的数建双向边,一个联通块如果是树就会有一个点不能选,那么一个区间同时包含这个联通块的最大值和最小值,就不能组成
-
可以维护一个ans[i]表示从i开始最大能到几,双指针就可以O(n)预处理出这个数组,每次查询就可以做到O(1)了
Show Code
#include <cstdio>
const int N = 2e5 + 5;
int read(int x = 0, int f = 1, char c = getchar()) {
for (; c < '0' || c > '9'; c = getchar())
if (c == '-') f = -1;
for (; c >='0' && c <='9'; c = getchar())
x = x * 10 + c - '0';
return x * f;
}
int n, m, f[N], s[N], b[N], ans[N];
int Find(int x) {
return x == f[x] ? x : (f[x] = Find(f[x]));
}
int main() {
freopen("playingcard.in", "r", stdin);
freopen("playingcard.out", "w", stdout);
n = read(); m = read();
for (int i = 1; i <= n; ++i) f[i] = i;
while (m--) {
int x = Find(read()), y = Find(read());
s[x]++;
if (x == y) continue;
f[y] = x; s[x] += s[y];
}
for (int i = 1, j = 1; i <= n; ++i) {
for (int x; j <= n && b[x=Find(j)] < s[x]; ++j) b[x]++;
ans[i] = j - 1; b[Find(i)]--;
}
m = read();
while (m--) {
int x = read();
puts(read() <= ans[x] ? "Yes" : "No");
}
return 0;
}
C 变化的树
-
对于x子树上的点y,它增加的值就是w-k(dep[y]-dep[x]) = w+kdep[x]-kdep[y],
-
对于每个点维护w+kdep[x]的和sw[i]和k的和sk[i],查询的时候就是sw[x]-dep[x]sk[x]
-
那就拿一个支持区间加,单点查的数据结构,其实树状数组就可以,然而我考场写的大常数线段树
Show Code
#include <cstdio>
const int N = 3e5 + 5, M = 1e9 + 7;
int read(int x = 0, int f = 1, char c = getchar()) {
for (; c < '0' || c > '9'; c = getchar())
if (c == '-') f = -1;
for (; c >= '0' && c <= '9'; c = getchar())
x = x * 10 + c - '0';
return x * f;
}
struct Edge {
int next, t;
}e[N];
int head[N], edc;
void Add(int x, int y) {
e[++edc] = (Edge) {head[x], y};
head[x] = edc;
}
int n, dep[N], dfc, dfn[N], a[N], siz[N], tw[N], tk[N];
void Dfs(int x, int fa) {
siz[x] = 1;
dep[x] = dep[fa] + 1;
dfn[x] = ++dfc; a[dfc] = x;
for (int i = head[x]; i; i = e[i].next) {
int y = e[i].t;
Dfs(y, x);
siz[x] += siz[y];
}
}
void Add(int x, int w, int k) {
for (; x <= n; x += x & -x) {
if ((tw[x] += w) >= M) tw[x] -= M;
if ((tk[x] += k) >= M) tk[x] -= M;
}
}
int Ask(int x, bool g, int s = 0) {
for (; x; x -= x & -x)
if ((s += (g ? tw[x] : tk[x])) >= M) s -= M;
return s;
}
int main() {
freopen("change.in", "r", stdin);
freopen("change.out", "w", stdout);
n = read();
for (int i = 2; i <= n; ++i)
Add(read(), i);
Dfs(1, 0);
int m = read();
while (m--) {
int od = read(), x = read();
if (od == 1) {
int w = read(), k = read();
w = (w + 1LL * dep[x] * k) % M;
Add(dfn[x], w, k); Add(dfn[x] + siz[x], -w, -k);
}
else printf("%lld\n", (Ask(dfn[x], 1) - 1LL * dep[x] * Ask(dfn[x], 0) % M + M) % M);
}
return 0;
}
D 炼金术 (Unaccepted)
- 神仙题爆0
Show Code