方格取数
看一眼复杂度,\(O(nm)\)级别的,考虑两个循环的 DP。分析整个题面之后发现,我们穿过边界时,当且仅当另一个连通块的权值比当前的大,否则便不会为了穿过边界而损失当前金块。所以,我们做一个 DP 一样的东西:因为发现每个格子只能走一遍,那么在每一列你只能选择一直向上走和一直向下走,所以这个 DP 便没了后效性,然后再处理连通块之间的连接判断就做完了。
int mp[MAX_N][MAX_N], n, m, T;
while (scanf("%d%d", &n, &m) != EOF)
for (int i = 1; i <= n; i++)
for (int j = 1; j <= m; j++)
for (int j = 1; j <= m; j++)
for (int i = 1; i <= n; i++)
cur = -1e18, dp[i][j] = cur;
cur = max(cur, dp[i][j - 1]) + mp[i][j], dp[i][j] = cur;
for (int i = n; i >= 1; i--)
cur = max(cur, dp[i][j - 1]) + mp[i][j], dp[i][j] = max(cur, dp[i][j]);
for (int i = n; i >= 1; i--)
else if (dp[i][j - 1] >= 0)
for (int i = 1; i < down_bound; i++)
cur += mp[i][j], dp[i][j] = max(dp[i][j], cur);
for (int i = 1; i <= n; i++)
else if (dp[i][j - 1] >= 0)
for (int i = n; i > up_bound; i--)
cur += mp[i][j], dp[i][j] = max(dp[i][j], cur);
for (int i = 1; i <= n; i++)
ans = max(ans, dp[i][m]);
// B.cpp
#include <bits/stdc++.h>
#define ll long long
using namespace std;
const int MAX_N = 1030;
int mp[MAX_N][MAX_N], n, m, T;
ll dp[MAX_N][MAX_N];
int main()
{
while (scanf("%d%d", &n, &m) != EOF)
{
for (int i = 1; i <= n; i++)
for (int j = 1; j <= m; j++)
scanf("%d", &mp[i][j]);
for (int j = 1; j <= m; j++)
{
ll cur = -1e18;
for (int i = 1; i <= n; i++)
if (mp[i][j] == -1)
cur = -1e18, dp[i][j] = cur;
else
cur = max(cur, dp[i][j - 1]) + mp[i][j], dp[i][j] = cur;
cur = -1e18;
for (int i = n; i >= 1; i--)
if (mp[i][j] == -1)
cur = -1e18;
else
cur = max(cur, dp[i][j - 1]) + mp[i][j], dp[i][j] = max(cur, dp[i][j]);
int down_bound = 0;
for (int i = n; i >= 1; i--)
if (mp[i][j] == -1)
break;
else if (dp[i][j - 1] >= 0)
{
down_bound = i;
break;
}
cur = 0;
for (int i = 1; i < down_bound; i++)
if (mp[i][j] == -1)
break;
else
cur += mp[i][j], dp[i][j] = max(dp[i][j], cur);
int up_bound = n + 1;
for (int i = 1; i <= n; i++)
if (mp[i][j] == -1)
break;
else if (dp[i][j - 1] >= 0)
{
up_bound = i;
break;
}
cur = 0;
for (int i = n; i > up_bound; i--)
if (mp[i][j] == -1)
break;
else
cur += mp[i][j], dp[i][j] = max(dp[i][j], cur);
}
ll ans = -1;
for (int i = 1; i <= n; i++)
ans = max(ans, dp[i][m]);
printf("%lld\n", ans);
}
return 0;
}
// B.cpp
#include <bits/stdc++.h>
#define ll long long
using namespace std;
const int MAX_N = 1030;
int mp[MAX_N][MAX_N], n, m, T;
ll dp[MAX_N][MAX_N];
int main()
{
while (scanf("%d%d", &n, &m) != EOF)
{
for (int i = 1; i <= n; i++)
for (int j = 1; j <= m; j++)
scanf("%d", &mp[i][j]);
for (int j = 1; j <= m; j++)
{
ll cur = -1e18;
for (int i = 1; i <= n; i++)
if (mp[i][j] == -1)
cur = -1e18, dp[i][j] = cur;
else
cur = max(cur, dp[i][j - 1]) + mp[i][j], dp[i][j] = cur;
cur = -1e18;
for (int i = n; i >= 1; i--)
if (mp[i][j] == -1)
cur = -1e18;
else
cur = max(cur, dp[i][j - 1]) + mp[i][j], dp[i][j] = max(cur, dp[i][j]);
int down_bound = 0;
for (int i = n; i >= 1; i--)
if (mp[i][j] == -1)
break;
else if (dp[i][j - 1] >= 0)
{
down_bound = i;
break;
}
cur = 0;
for (int i = 1; i < down_bound; i++)
if (mp[i][j] == -1)
break;
else
cur += mp[i][j], dp[i][j] = max(dp[i][j], cur);
int up_bound = n + 1;
for (int i = 1; i <= n; i++)
if (mp[i][j] == -1)
break;
else if (dp[i][j - 1] >= 0)
{
up_bound = i;
break;
}
cur = 0;
for (int i = n; i > up_bound; i--)
if (mp[i][j] == -1)
break;
else
cur += mp[i][j], dp[i][j] = max(dp[i][j], cur);
}
ll ans = -1;
for (int i = 1; i <= n; i++)
ans = max(ans, dp[i][m]);
printf("%lld\n", ans);
}
return 0;
}
染色
真的是您
的签到题。
首先,我们可以枚举有\(i\)个\(A\),然后判断剩下的是否整除\(B\)。如果整除,那么就可以加入答案,考虑计算部分贡献,在\(n\)个砖块里选择\(i\)个作为\(A\)的,剩下再乘上\(n\)个砖块里面选择\(\frac{n – iA}{B}\)个,如果重叠的就算做是\(C\),如果非重叠就是\(B\)。
const int MAX_N = 3e5 + 200, mod = 998244353;
int fac[MAX_N], fac_inv[MAX_N], T;
int quick_pow(int bas, int tim)
ans = 1LL * ans * bas % mod;
bas = 1LL * bas * bas % mod;
int combinator(int n_, int k_)
if (n_ < k_ || n_ < 0 || k_ < 0)
return 1LL * fac[n_] * fac_inv[k_] % mod * fac_inv[n_ - k_] % mod;
for (int i = fac[0] = 1; i < MAX_N; i++)
fac[i] = 1LL * fac[i - 1] * i % mod;
fac_inv[MAX_N - 1] = quick_pow(fac[MAX_N - 1], mod - 2);
for (int i = MAX_N - 2; i >= 0; i--)
fac_inv[i] = 1LL * fac_inv[i + 1] * (i + 1) % mod;
scanf("%lld%lld%lld%lld", &n, &A, &B, &x);
for (int i = 0; i <= n; i++)
ll tmp = x - 1LL * i * A;
if (tmp > 0 && (tmp % B == 0))
ans = (1LL * ans + 1LL * combinator(n, i) * combinator(n, tmp / B) % mod) % mod;
// A.cpp
#include <bits/stdc++.h>
#define ll long long
using namespace std;
const int MAX_N = 3e5 + 200, mod = 998244353;
int fac[MAX_N], fac_inv[MAX_N], T;
ll n, A, B, x;
int quick_pow(int bas, int tim)
{
int ans = 1;
while (tim)
{
if (tim & 1)
ans = 1LL * ans * bas % mod;
bas = 1LL * bas * bas % mod;
tim >>= 1;
}
return ans;
}
int combinator(int n_, int k_)
{
if (n_ < k_ || n_ < 0 || k_ < 0)
return 0;
return 1LL * fac[n_] * fac_inv[k_] % mod * fac_inv[n_ - k_] % mod;
}
int main()
{
scanf("%d", &T);
for (int i = fac[0] = 1; i < MAX_N; i++)
fac[i] = 1LL * fac[i - 1] * i % mod;
fac_inv[MAX_N - 1] = quick_pow(fac[MAX_N - 1], mod - 2);
for (int i = MAX_N - 2; i >= 0; i--)
fac_inv[i] = 1LL * fac_inv[i + 1] * (i + 1) % mod;
while (T--)
{
scanf("%lld%lld%lld%lld", &n, &A, &B, &x);
ll res = 0, ans = 0;
for (int i = 0; i <= n; i++)
{
ll tmp = x - 1LL * i * A;
if (tmp > 0 && (tmp % B == 0))
ans = (1LL * ans + 1LL * combinator(n, i) * combinator(n, tmp / B) % mod) % mod;
}
printf("%lld\n", ans);
}
return 0;
}
// A.cpp
#include <bits/stdc++.h>
#define ll long long
using namespace std;
const int MAX_N = 3e5 + 200, mod = 998244353;
int fac[MAX_N], fac_inv[MAX_N], T;
ll n, A, B, x;
int quick_pow(int bas, int tim)
{
int ans = 1;
while (tim)
{
if (tim & 1)
ans = 1LL * ans * bas % mod;
bas = 1LL * bas * bas % mod;
tim >>= 1;
}
return ans;
}
int combinator(int n_, int k_)
{
if (n_ < k_ || n_ < 0 || k_ < 0)
return 0;
return 1LL * fac[n_] * fac_inv[k_] % mod * fac_inv[n_ - k_] % mod;
}
int main()
{
scanf("%d", &T);
for (int i = fac[0] = 1; i < MAX_N; i++)
fac[i] = 1LL * fac[i - 1] * i % mod;
fac_inv[MAX_N - 1] = quick_pow(fac[MAX_N - 1], mod - 2);
for (int i = MAX_N - 2; i >= 0; i--)
fac_inv[i] = 1LL * fac_inv[i + 1] * (i + 1) % mod;
while (T--)
{
scanf("%lld%lld%lld%lld", &n, &A, &B, &x);
ll res = 0, ans = 0;
for (int i = 0; i <= n; i++)
{
ll tmp = x - 1LL * i * A;
if (tmp > 0 && (tmp % B == 0))
ans = (1LL * ans + 1LL * combinator(n, i) * combinator(n, tmp / B) % mod) % mod;
}
printf("%lld\n", ans);
}
return 0;
}
字符串
这道题其实比较好想,思考计算子序列的递推公式:
\[ dp[i] = 2dp[i – 1] – dp[lastPos[str[i]]] \]
先计算前缀的子序列个数,然后在考虑向后构造:我们发现,DP 数组是由单调递增的性质的,那么我们每次转移的时候取最小的\(lastPos[char]\)即可。
const int MAX_K = 27, mod = 1e9 + 7;
int dp[MAX_K], n, m, k, prev_pos[MAX_K], ans;
scanf("%d %d\n", &m, &k);
ch = getchar(), x = ch - 'a' + 1;
ans = 2LL * ans % mod, ans = (1LL * ans + mod - dp[x]) % mod;
dp[x] = old, prev_pos[x] = ++n;
for (int i = 1; i <= m; i++)
for (int j = 1; j <= k; j++)
if (!x || prev_pos[x] > prev_pos[j])
prev_pos[x] = ++n, old = ans;
ans = 2LL * ans % mod, ans = (ans + mod - dp[x]) % mod;
// C.cpp
#include <bits/stdc++.h>
using namespace std;
const int MAX_K = 27, mod = 1e9 + 7;
int dp[MAX_K], n, m, k, prev_pos[MAX_K], ans;
int main()
{
scanf("%d %d\n", &m, &k);
int ans = 1, x, old;
char ch;
while (true)
{
ch = getchar(), x = ch - 'a' + 1;
if (x < 1 || x > 26)
break;
old = ans;
ans = 2LL * ans % mod, ans = (1LL * ans + mod - dp[x]) % mod;
dp[x] = old, prev_pos[x] = ++n;
}
for (int i = 1; i <= m; i++)
{
x = 0;
for (int j = 1; j <= k; j++)
if (!x || prev_pos[x] > prev_pos[j])
x = j;
prev_pos[x] = ++n, old = ans;
ans = 2LL * ans % mod, ans = (ans + mod - dp[x]) % mod;
dp[x] = old;
}
printf("%d", ans);
return 0;
}
// C.cpp
#include <bits/stdc++.h>
using namespace std;
const int MAX_K = 27, mod = 1e9 + 7;
int dp[MAX_K], n, m, k, prev_pos[MAX_K], ans;
int main()
{
scanf("%d %d\n", &m, &k);
int ans = 1, x, old;
char ch;
while (true)
{
ch = getchar(), x = ch - 'a' + 1;
if (x < 1 || x > 26)
break;
old = ans;
ans = 2LL * ans % mod, ans = (1LL * ans + mod - dp[x]) % mod;
dp[x] = old, prev_pos[x] = ++n;
}
for (int i = 1; i <= m; i++)
{
x = 0;
for (int j = 1; j <= k; j++)
if (!x || prev_pos[x] > prev_pos[j])
x = j;
prev_pos[x] = ++n, old = ans;
ans = 2LL * ans % mod, ans = (ans + mod - dp[x]) % mod;
dp[x] = old;
}
printf("%d", ans);
return 0;
}
膜方俱乐部
sb 基环树,记录下环的值还有链上的值,然后就可以愉快的 AC 了。
const int MAX_N = 200100;
int head[MAX_N], current, deg[MAX_N], n, val[MAX_N], deletion[MAX_N], mem[MAX_N], sum[MAX_N], loop[MAX_N];
int find(int x) { return x == mem[x] ? x : mem[x] = find(mem[x]); }
void addpath(int src, int dst)
edges[current].to = dst, edges[current].nxt = head[src];
for (int i = 1; i <= n; i++)
for (int i = head[u]; i != -1; i = edges[i].nxt)
if (--deg[edges[i].to] == 0)
for (int i = 1; i <= n; i++)
for (int i = head[u]; i != -1; i = edges[i].nxt)
if (edges[i].to != fat && deg[edges[i].to] <= 0)
memset(head, -1, sizeof(head));
for (int i = 1; i <= n; i++)
scanf("%d", &val[i]), mem[i] = i, sum[i] = val[i];
for (int i = 1, v; i <= n; i++)
scanf("%d", &v), addpath(i, v), addpath(v, i), deg[v]++;
int x = find(i), y = find(v);
mem[y] = x, sum[x] += sum[y];
for (int i = 1; i <= n; i++)
for (int i = 1; i <= n; i++)
printf("%d\n", loop[find(i)]);
printf("%d\n", val[i] + loop[find(i)]);
// B.cpp
#include <bits/stdc++.h>
using namespace std;
const int MAX_N = 200100;
int head[MAX_N], current, deg[MAX_N], n, val[MAX_N], deletion[MAX_N], mem[MAX_N], sum[MAX_N], loop[MAX_N];
struct edge
{
int to, nxt;
} edges[MAX_N << 1];
int find(int x) { return x == mem[x] ? x : mem[x] = find(mem[x]); }
void addpath(int src, int dst)
{
edges[current].to = dst, edges[current].nxt = head[src];
head[src] = current++;
}
void toposort()
{
queue<int> q;
for (int i = 1; i <= n; i++)
if (deg[i] == 0)
q.push(i);
while (!q.empty())
{
int u = q.front();
q.pop(), deg[u]--;
for (int i = head[u]; i != -1; i = edges[i].nxt)
if (--deg[edges[i].to] == 0)
q.push(edges[i].to);
}
for (int i = 1; i <= n; i++)
if (deg[i] > 0)
loop[find(i)] += val[i];
}
void dfs(int u, int fat)
{
if (deg[fat] <= 0)
val[u] += val[fat];
for (int i = head[u]; i != -1; i = edges[i].nxt)
if (edges[i].to != fat && deg[edges[i].to] <= 0)
dfs(edges[i].to, u);
}
int main()
{
memset(head, -1, sizeof(head));
scanf("%d", &n);
for (int i = 1; i <= n; i++)
scanf("%d", &val[i]), mem[i] = i, sum[i] = val[i];
for (int i = 1, v; i <= n; i++)
{
scanf("%d", &v), addpath(i, v), addpath(v, i), deg[v]++;
int x = find(i), y = find(v);
if (x != y)
mem[y] = x, sum[x] += sum[y];
}
toposort();
for (int i = 1; i <= n; i++)
if (deg[i] > 0)
dfs(i, 0);
for (int i = 1; i <= n; i++)
if (deg[i] > 0)
printf("%d\n", loop[find(i)]);
else
printf("%d\n", val[i] + loop[find(i)]);
return 0;
}
// B.cpp
#include <bits/stdc++.h>
using namespace std;
const int MAX_N = 200100;
int head[MAX_N], current, deg[MAX_N], n, val[MAX_N], deletion[MAX_N], mem[MAX_N], sum[MAX_N], loop[MAX_N];
struct edge
{
int to, nxt;
} edges[MAX_N << 1];
int find(int x) { return x == mem[x] ? x : mem[x] = find(mem[x]); }
void addpath(int src, int dst)
{
edges[current].to = dst, edges[current].nxt = head[src];
head[src] = current++;
}
void toposort()
{
queue<int> q;
for (int i = 1; i <= n; i++)
if (deg[i] == 0)
q.push(i);
while (!q.empty())
{
int u = q.front();
q.pop(), deg[u]--;
for (int i = head[u]; i != -1; i = edges[i].nxt)
if (--deg[edges[i].to] == 0)
q.push(edges[i].to);
}
for (int i = 1; i <= n; i++)
if (deg[i] > 0)
loop[find(i)] += val[i];
}
void dfs(int u, int fat)
{
if (deg[fat] <= 0)
val[u] += val[fat];
for (int i = head[u]; i != -1; i = edges[i].nxt)
if (edges[i].to != fat && deg[edges[i].to] <= 0)
dfs(edges[i].to, u);
}
int main()
{
memset(head, -1, sizeof(head));
scanf("%d", &n);
for (int i = 1; i <= n; i++)
scanf("%d", &val[i]), mem[i] = i, sum[i] = val[i];
for (int i = 1, v; i <= n; i++)
{
scanf("%d", &v), addpath(i, v), addpath(v, i), deg[v]++;
int x = find(i), y = find(v);
if (x != y)
mem[y] = x, sum[x] += sum[y];
}
toposort();
for (int i = 1; i <= n; i++)
if (deg[i] > 0)
dfs(i, 0);
for (int i = 1; i <= n; i++)
if (deg[i] > 0)
printf("%d\n", loop[find(i)]);
else
printf("%d\n", val[i] + loop[find(i)]);
return 0;
}
排名
为什么可以这么简单啊?
int n, m, prefix, suffix;
for (int i = 1, p; i <= n; i++)
scanf("%d", &p), prefix += p - 1, suffix += m - p;
printf("%d\n%d\n", max(1, m - suffix), min(m, prefix + 1));
// A.cpp
#include <bits/stdc++.h>
using namespace std;
int n, m, prefix, suffix;
int main()
{
scanf("%d%d", &n, &m);
for (int i = 1, p; i <= n; i++)
scanf("%d", &p), prefix += p - 1, suffix += m - p;
printf("%d\n%d\n", max(1, m - suffix), min(m, prefix + 1));
return 0;
}
// A.cpp
#include <bits/stdc++.h>
using namespace std;
int n, m, prefix, suffix;
int main()
{
scanf("%d%d", &n, &m);
for (int i = 1, p; i <= n; i++)
scanf("%d", &p), prefix += p - 1, suffix += m - p;
printf("%d\n%d\n", max(1, m - suffix), min(m, prefix + 1));
return 0;
}
或者过于完美亦未可知
这道题就是数据结构和思维结合的题目。题目求的是:怎样安排\(q\)个操作,使得最后的串与目标串相差最小。
首先,把答案定为目标串中\(0\)的个数,假定整个序列完全覆盖了\(1\)。我们要求的答案就是尽量把这个答案减干净(也就是求最小值)。所以,对于每一个操作,我们都在他的左端点和右端点上打上一个标记,来标注最少能少多少个,然后这道题就可以解决了(有点像最小割的方法)。
const int MAX_N = 800010, INF = 0x3f3f3f3f;
int min_val[MAX_N], seq[MAX_N], n, q, ans;
vector<int> queries[MAX_N];
#define mid ((l + r) >> 1)
#define rson ((p << 1) | 1)
min_val[p] = min(min_val[lson], min_val[rson]);
void build(int l, int r, int p)
return (void)(min_val[p] = ((l == 0) ? 0 : INF));
build(l, mid, lson), build(mid + 1, r, rson);
void update(int qx, int l, int r, int p, int val)
return (void)(min_val[p] = min(min_val[p], val));
update(qx, l, mid, lson, val);
update(qx, mid + 1, r, rson, val);
int query(int ql, int qr, int l, int r, int p)
ret = min(ret, query(ql, qr, l, mid, lson));
ret = min(ret, query(ql, qr, mid + 1, r, rson));
for (int i = 0; i < n; i++)
scanf("%d", &seq[i]), ans += !seq[i];
for (int i = 1, x, y; i <= q; i++)
scanf("%d%d", &x, &y), queries[x - 1].push_back(y);
for (int i = 0; i < n; i++)
for (int j = 0, siz = queries[i].size(); j < siz; j++)
update(queries[i][j], 0, n, 1, query(i, queries[i][j], 0, n, 1));
update(i + 1, 0, n, 1, query(i, i, 0, n, 1) + (seq[i] ? 1 : -1));
printf("%d\n", ans + query(n, n, 0, n, 1));
// C.cpp
#include <bits/stdc++.h>
using namespace std;
const int MAX_N = 800010, INF = 0x3f3f3f3f;
int min_val[MAX_N], seq[MAX_N], n, q, ans;
vector<int> queries[MAX_N];
#define mid ((l + r) >> 1)
#define lson (p << 1)
#define rson ((p << 1) | 1)
void pushup(int p)
{
min_val[p] = min(min_val[lson], min_val[rson]);
}
void build(int l, int r, int p)
{
if (l == r)
return (void)(min_val[p] = ((l == 0) ? 0 : INF));
build(l, mid, lson), build(mid + 1, r, rson);
pushup(p);
}
void update(int qx, int l, int r, int p, int val)
{
if (l == r)
return (void)(min_val[p] = min(min_val[p], val));
if (qx <= mid)
update(qx, l, mid, lson, val);
else
update(qx, mid + 1, r, rson, val);
pushup(p);
}
int query(int ql, int qr, int l, int r, int p)
{
if (ql <= l && r <= qr)
return min_val[p];
int ret = INF;
if (ql <= mid)
ret = min(ret, query(ql, qr, l, mid, lson));
if (mid < qr)
ret = min(ret, query(ql, qr, mid + 1, r, rson));
return ret;
}
#undef mid
#undef lson
#undef rson
int main()
{
scanf("%d", &n);
for (int i = 0; i < n; i++)
scanf("%d", &seq[i]), ans += !seq[i];
scanf("%d", &q);
for (int i = 1, x, y; i <= q; i++)
scanf("%d%d", &x, &y), queries[x - 1].push_back(y);
build(0, n, 1);
for (int i = 0; i < n; i++)
{
for (int j = 0, siz = queries[i].size(); j < siz; j++)
update(queries[i][j], 0, n, 1, query(i, queries[i][j], 0, n, 1));
update(i + 1, 0, n, 1, query(i, i, 0, n, 1) + (seq[i] ? 1 : -1));
}
printf("%d\n", ans + query(n, n, 0, n, 1));
return 0;
}
// C.cpp
#include <bits/stdc++.h>
using namespace std;
const int MAX_N = 800010, INF = 0x3f3f3f3f;
int min_val[MAX_N], seq[MAX_N], n, q, ans;
vector<int> queries[MAX_N];
#define mid ((l + r) >> 1)
#define lson (p << 1)
#define rson ((p << 1) | 1)
void pushup(int p)
{
min_val[p] = min(min_val[lson], min_val[rson]);
}
void build(int l, int r, int p)
{
if (l == r)
return (void)(min_val[p] = ((l == 0) ? 0 : INF));
build(l, mid, lson), build(mid + 1, r, rson);
pushup(p);
}
void update(int qx, int l, int r, int p, int val)
{
if (l == r)
return (void)(min_val[p] = min(min_val[p], val));
if (qx <= mid)
update(qx, l, mid, lson, val);
else
update(qx, mid + 1, r, rson, val);
pushup(p);
}
int query(int ql, int qr, int l, int r, int p)
{
if (ql <= l && r <= qr)
return min_val[p];
int ret = INF;
if (ql <= mid)
ret = min(ret, query(ql, qr, l, mid, lson));
if (mid < qr)
ret = min(ret, query(ql, qr, mid + 1, r, rson));
return ret;
}
#undef mid
#undef lson
#undef rson
int main()
{
scanf("%d", &n);
for (int i = 0; i < n; i++)
scanf("%d", &seq[i]), ans += !seq[i];
scanf("%d", &q);
for (int i = 1, x, y; i <= q; i++)
scanf("%d%d", &x, &y), queries[x - 1].push_back(y);
build(0, n, 1);
for (int i = 0; i < n; i++)
{
for (int j = 0, siz = queries[i].size(); j < siz; j++)
update(queries[i][j], 0, n, 1, query(i, queries[i][j], 0, n, 1));
update(i + 1, 0, n, 1, query(i, i, 0, n, 1) + (seq[i] ? 1 : -1));
}
printf("%d\n", ans + query(n, n, 0, n, 1));
return 0;
}
献给许许多多的忌日
先求一遍树上最大独立集,然后计算一下:如果独立集的两倍都小于\(n\),那么对于警察来讲可以建造更多无法起火的房屋;如果独立集大于\(k\),那么断边也行不通;
int head[MAX_N], current, n, k, dp[MAX_N][2];
void addpath(int src, int dst)
edges[current].to = dst, edges[current].nxt = head[src];
for (int i = head[u]; i != -1; i = edges[i].nxt)
dfs(edges[i].to, u), dp[u][0] += max(dp[edges[i].to][1], dp[edges[i].to][0]);
for (int i = head[u]; i != -1; i = edges[i].nxt)
dp[u][1] = max(dp[u][1], dp[u][0] - max(dp[edges[i].to][1], dp[edges[i].to][0]) + dp[edges[i].to][0] + 1);
memset(head, -1, sizeof(head));
for (int i = 1, u, v; i <= n - 1; i++)
scanf("%d%d", &u, &v), addpath(u, v), addpath(v, u);
int independent_set = max(dp[1][0], dp[1][1]);
if ((independent_set << 1) < n || k < independent_set - 1)
// A.cpp
#include <bits/stdc++.h>
using namespace std;
const int MAX_N = 5050;
int head[MAX_N], current, n, k, dp[MAX_N][2];
struct edge
{
int to, nxt;
} edges[MAX_N << 1];
void addpath(int src, int dst)
{
edges[current].to = dst, edges[current].nxt = head[src];
head[src] = current++;
}
void dfs(int u, int fa)
{
for (int i = head[u]; i != -1; i = edges[i].nxt)
if (edges[i].to != fa)
dfs(edges[i].to, u), dp[u][0] += max(dp[edges[i].to][1], dp[edges[i].to][0]);
for (int i = head[u]; i != -1; i = edges[i].nxt)
if (edges[i].to != fa)
dp[u][1] = max(dp[u][1], dp[u][0] - max(dp[edges[i].to][1], dp[edges[i].to][0]) + dp[edges[i].to][0] + 1);
}
int main()
{
memset(head, -1, sizeof(head));
scanf("%d%d", &n, &k);
for (int i = 1, u, v; i <= n - 1; i++)
scanf("%d%d", &u, &v), addpath(u, v), addpath(v, u);
dfs(1, 0);
int independent_set = max(dp[1][0], dp[1][1]);
if ((independent_set << 1) < n || k < independent_set - 1)
puts("policeman");
else
puts("arsonist");
return 0;
}
// A.cpp
#include <bits/stdc++.h>
using namespace std;
const int MAX_N = 5050;
int head[MAX_N], current, n, k, dp[MAX_N][2];
struct edge
{
int to, nxt;
} edges[MAX_N << 1];
void addpath(int src, int dst)
{
edges[current].to = dst, edges[current].nxt = head[src];
head[src] = current++;
}
void dfs(int u, int fa)
{
for (int i = head[u]; i != -1; i = edges[i].nxt)
if (edges[i].to != fa)
dfs(edges[i].to, u), dp[u][0] += max(dp[edges[i].to][1], dp[edges[i].to][0]);
for (int i = head[u]; i != -1; i = edges[i].nxt)
if (edges[i].to != fa)
dp[u][1] = max(dp[u][1], dp[u][0] - max(dp[edges[i].to][1], dp[edges[i].to][0]) + dp[edges[i].to][0] + 1);
}
int main()
{
memset(head, -1, sizeof(head));
scanf("%d%d", &n, &k);
for (int i = 1, u, v; i <= n - 1; i++)
scanf("%d%d", &u, &v), addpath(u, v), addpath(v, u);
dfs(1, 0);
int independent_set = max(dp[1][0], dp[1][1]);
if ((independent_set << 1) < n || k < independent_set - 1)
puts("policeman");
else
puts("arsonist");
return 0;
}