JLOI2016 简要题解
「JLOI2016」侦查守卫
题意
有一个 \(n\) 个点的树,有 \(m\) 个关键点需要被监视。可以在其中一些点上插眼,在 \(i\) 号点上放眼需要花费 \(w_i\) 的代价,可以监视距离 \(i\) 不超过 \(d\) 的所有点。
问将所有关键点都被监视所需要花费的最小代价。
\(m \le n \le 5 \times 10^5, d \le 20, w_i \le 1000\)
题解
\(d\) 很小,不难想到 \(\mathcal O(nd)\) 的 \(dp\) 。
令 \(f_{i, j}\) 为 \(i\) 向下 \(j\) 层有未被监视的点的最小代价。
令 \(g_{i, j}\) 为 \(i\) 向上 \(j\) 层都能被监视的最小代价。
状态很容易想。。但是转移就很恶心了。。
- 如果点 \(u\) 一定要被监视,那么令 \(f_{u, 0} = g_{u, 0} = w_u\) ,表示这个点被监视的代价。
然后从它的儿子 \(v\) 转移状态上来。
那么对于 \(g\) 有如下转移:
\]
前者意味着对于子树 \(v\) 向上 \(j\) 层都被此处覆盖了,后者就是考虑有一个儿子 \(v\) 能向上延伸 \(j + 1\) 层。
然后记得后缀 chkmin
。
对于 \(f\) 你就前缀 chkmin
并且注意 f[u][0] = g[u][0]
。
总结
对于有些最优化 \(dp\) ,可以记一下前缀 or 后缀 \(\min\) 的答案,转移会简单很多。。
代码
#include <bits/stdc++.h>
#define For(i, l, r) for (register int i = (l), i##end = (int)(r); i <= i##end; ++i)
#define Fordown(i, r, l) for (register int i = (r), i##end = (int)(l); i >= i##end; --i)
#define Rep(i, r) for (register int i = (0), i##end = (int)(r); i < i##end; ++i)
#define Set(a, v) memset(a, v, sizeof(a))
#define Cpy(a, b) memcpy(a, b, sizeof(a))
#define debug(x) cout << #x << ": " << (x) << endl
using namespace std;
template<typename T> inline bool chkmin(T &a, T b) { return b < a ? a = b, 1 : 0; }
template<typename T> inline bool chkmax(T &a, T b) { return b > a ? a = b, 1 : 0; }
inline int read() {
int x(0), sgn(1); char ch(getchar());
for (; !isdigit(ch); ch = getchar()) if (ch == '-') sgn = -1;
for (; isdigit(ch); ch = getchar()) x = (x * 10) + (ch ^ 48);
return x * sgn;
}
void File() {
#ifdef zjp_shadow
freopen ("2024.in", "r", stdin);
freopen ("2024.out", "w", stdout);
#endif
}
const int N = 500100, inf = 0x3f3f3f3f;
int n, d, f[N][25], g[N][25];
vector<int> G[N];
int w[N]; bool App[N];
void Dp(int u, int fa) {
if (App[u]) f[u][0] = g[u][0] = w[u];
For (i, 1, d) g[u][i] = w[u]; g[u][d + 1] = inf;
for (int v : G[u]) if (v != fa) {
Dp(v, u);
Fordown (i, d, 0)
g[u][i] = min(g[u][i + 1], min(g[u][i] + f[v][i], g[v][i + 1] + f[u][i + 1]));
f[u][0] = g[u][0];
For (i, 1, d)
f[u][i] = min(f[u][i] + f[v][i - 1], f[u][i - 1]);
}
}
int main () {
File();
n = read(); d = read();
For (i, 1, n) w[i] = read();
For (i, 1, read()) App[read()] = true;
For (i, 1, n - 1) {
int u = read(), v = read();
G[u].push_back(v); G[v].push_back(u);
}
Dp(1, 0);
printf ("%d\n", f[1][0]);
return 0;
}
「JLOI2016」方
题意
给你 \(n \times m\) 的方格图,有 \((n + 1) \times (m + 1)\) 个格点,禁止其中 \(k\) 个格点作为端点,问剩下的图有多少个格点正方形。(斜的也算)
\(n, m \le 10^6, k \le 2 \times 10^3\)
题解
竟然还有这种 shit 题。
\(k = 0\) 是我原来在 \(\text{NOIp}\) 模拟赛里面搬的一道水题。。。
然后有禁止的限制,不难想到容斥,然后后面的细节就贼烦了。
具体可以看看 zzq 的博客 ,因为太麻烦了,不想讲了。
前面那些优化并不是重点,重点在于如何容斥。。
你可以考虑对于单个正方形会被统计几次,然后依次凑系数就行啦。
zzq 那个按顺序枚举的方法十分优秀,不需要开
hash
表存状态,直接最后除掉一个正方形被算进去的次数就行啦。
总结
容斥考虑对于一个点会被计算几次,然后凑系数就行了。
代码
#include <bits/stdc++.h>
#define For(i, l, r) for (register int i = (l), i##end = (int)(r); i <= i##end; ++i)
#define Fordown(i, r, l) for (register int i = (r), i##end = (int)(l); i >= i##end; --i)
#define Rep(i, r) for (register int i = (0), i##end = (int)(r); i < i##end; ++i)
#define Set(a, v) memset(a, v, sizeof(a))
#define Cpy(a, b) memcpy(a, b, sizeof(a))
#define debug(x) cout << #x << ": " << (x) << endl
#define epb emplace_back
using namespace std;
template<typename T> inline bool chkmin(T &a, T b) { return b < a ? a = b, 1 : 0; }
template<typename T> inline bool chkmax(T &a, T b) { return b > a ? a = b, 1 : 0; }
inline int read() {
int x(0), sgn(1); char ch(getchar());
for (; !isdigit(ch); ch = getchar()) if (ch == '-') sgn = -1;
for (; isdigit(ch); ch = getchar()) x = (x * 10) + (ch ^ 48);
return x * sgn;
}
void File() {
#ifdef zjp_shadow
freopen ("2025.in", "r", stdin);
freopen ("2025.out", "w", stdout);
#endif
}
const int Mod = 1e8 + 7;
using ll = long long;
using PII = pair<int, int>;
const int N = 2010;
int n, m, k, x[N], y[N];
inline int Count(int l, int r, int d) {
if (!l || !r || !d) return 0;
int res = 0, upp = min(l + r, d), pos[3] = {l + 1, r + 1, upp}, cl = 1;
sort(pos, pos + 3);
Rep (i, 3) {
int cr = pos[i]; if (cr > upp) break;
if (cr < 2 || cl == cr) continue; ++ cl;
int vl = min(r, cl - 1) - max(cl - l, 1) + 1,
vr = min(r, cr - 1) - max(cr - l, 1) + 1;
res = (res + 1ll * (vl + vr) * (cr - cl + 1) / 2) % Mod; cl = cr;
}
return res;
}
inline int Calc(int u, int d, int l, int r) {
return (Count(l, r, d) + Count(l, r, u) + Count(u, d, l) + Count(u, d, r)
+ min(l, d) + min(d, r) + min(r, u) + min(u, l)) % Mod;
}
const double eps = 1e-9;
inline bool check(double x, double y) {
if (fabs(x - int(x + 0.5)) >= eps || fabs(y - int(y + 0.5)) >= eps) return false;
int nx = int(x + 0.5), ny = int(y + 0.5);
return 0 <= nx && nx <= n && 0 <= ny && y <= m;
}
inline int check(PII S) {
return 0 <= S.first && S.first <= n && 0 <= S.second && S.second <= m;
}
set<PII> T;
struct Node { int x, y; } P[N];
int main () {
File();
n = read(); m = read(); k = read();
int ans = 0;
For (i, 1, min(n, m))
ans = (ans + 1ll * i * (n - i + 1) % Mod * (m - i + 1)) % Mod;
For (i, 1, k) {
P[i].x = x[i] = read(), P[i].y = y[i] = read();
T.insert(make_pair(x[i], y[i]));
ans = (ans - Calc(x[i], n - x[i], y[i], m - y[i]) + Mod) % Mod;
}
sort(P + 1, P + k + 1, [&](Node a, Node b) { return a.x != b.x ? a.x < b.x : a.y < b.y; } );
For (i, 1, k) x[i] = P[i].x, y[i] = P[i].y;
int cnt3 = 0, cnt4 = 0;
For (i, 1, k) For (j, i + 1, k) {
{
double midx = (x[i] + x[j]) / 2.0, midy = (y[i] + y[j]) / 2.0,
gapx = x[i] - midx, gapy = y[i] - midy;
if (check(midx - gapy, midy + gapx) && check(midx + gapy, midy - gapx)) {
++ ans;
}
}
for (int dir = -1; dir <= 1; dir += 2) {
int tx = x[i] - x[j], ty = y[i] - y[j];
PII T1 = make_pair(x[i] - ty * dir, y[i] + tx * dir);
PII T2 = make_pair(x[j] - ty * dir, y[j] + tx * dir);
if (!check(T1) || !check(T2)) continue;
int cnt = 0; ++ ans;
if (T.find(T1) != T.end()) ++ cnt;
if (T.find(T2) != T.end()) ++ cnt;
cnt3 += (cnt >= 1);
cnt4 += (cnt >= 2);
}
}
ans -= cnt3 / 2 + cnt4 / 4;
printf ("%d\n", (ans % Mod + Mod) % Mod);
return 0;
}
「JLOI2016」成绩比较
题意
有 \(n\) 个人 \(m\) 门学科,第 \(i\) 门的分数为不大于 \(u_i\) 的一个正整数。
定义 \(A\) 碾压 \(B\) 当且仅当 \(A\) 的每门学科的分数都不低于 \(B\) 的该门学科的分数。
已知第一个人第 \(i\) 门学科的排名为 \(r_i\) ,即这门学科不低于 \(n − r_i\) 人的分数,但一定低于 \(r_i−1\) 人的分数。
求有多少种方案使得第一个人恰好碾压了 \(k\) 个人。
两种方案不同当且仅当存在两个人的分数不同。
\(n \le 100, m \le 100, u_i \le 10^9\)
题解
原来听 h10 讲广义容斥的时候讲过。。现在不会做了。。
令 \(g_x\) 为第一个人至少碾压了 \(x\) 个人的方案数。
其实就是
\]
其中 \(A_i\) 为对于第 \(i\) 门来说,所有人从小到大排分数合法的方案数。其余的意义就十分明显了,首先是选人,然后选剩下的人填这门比第一个人高的方案数。
其实就是
\]
这个显然是一个 \(n\) 次多项式,求出 \(n + 1\) 个点值,利用拉格朗日插值即可在 \(\mathcal O(n^2)\) 的时间内插出来,可以利用连续点值的性质优化到 \(\mathcal O(n)\) 。
最后不优化复杂度是 \(\mathcal O(n^2m)\) 的,已经完全足够了。
总结
计数题还是有很多式子可以形式化地写出来的。对于 至少 的方案数进行计算的时候,只需要考虑把不合法的位置全都用别的塞上去,那么剩下的就一定合法了。
代码
#include <bits/stdc++.h>
#define For(i, l, r) for (register int i = (l), i##end = (int)(r); i <= i##end; ++i)
#define Fordown(i, r, l) for (register int i = (r), i##end = (int)(l); i >= i##end; --i)
#define Rep(i, r) for (register int i = (0), i##end = (int)(r); i < i##end; ++i)
#define Set(a, v) memset(a, v, sizeof(a))
#define Cpy(a, b) memcpy(a, b, sizeof(a))
#define debug(x) cout << #x << ": " << (x) << endl
using namespace std;
template<typename T> inline bool chkmin(T &a, T b) { return b < a ? a = b, 1 : 0; }
template<typename T> inline bool chkmax(T &a, T b) { return b > a ? a = b, 1 : 0; }
inline int read() {
int x(0), sgn(1); char ch(getchar());
for (; !isdigit(ch); ch = getchar()) if (ch == '-') sgn = -1;
for (; isdigit(ch); ch = getchar()) x = (x * 10) + (ch ^ 48);
return x * sgn;
}
void File() {
#ifdef zjp_shadow
freopen ("2026.in", "r", stdin);
freopen ("2026.out", "w", stdout);
#endif
}
const int N = 110, Mod = 1e9 + 7;
int n, m, k, u[N], r[N];
inline int fpm(int x, int power) {
int res = 1;
for (; power; power >>= 1, x = 1ll * x * x % Mod)
if (power & 1) res = 1ll * res * x % Mod;
return res;
}
int x[N], y[N];
int Inter(int maxn, int p) {
int res = 0;
For (i, 1, maxn) {
int cur = 1, coef = 1;
For (j, 1, maxn) if (i != j) {
cur = 1ll * cur * (p - x[j]) % Mod;
coef = 1ll * coef * (x[i] - x[j]) % Mod;
}
res = (res + 1ll * y[i] * cur % Mod * fpm(coef, Mod - 2)) % Mod;
}
return res;
}
inline int Calc(int p) {
For (i, 1, n + 1)
x[i] = i, y[i] = (y[i - 1] + 1ll * fpm(u[p] - i, r[p] - 1) * fpm(i, n - r[p])) % Mod;
return Inter(n + 1, u[p]);
}
int g[N], A[N], Comb[N][N];
int main () {
File();
n = read(); m = read(); k = read();
For (i, 0, n) {
Comb[i][0] = 1;
For (j, 1, i)
Comb[i][j] = (Comb[i - 1][j - 1] + Comb[i - 1][j]) % Mod;
}
For (i, 1, m) u[i] = read();
For (i, 1, m) r[i] = read();
For (i, 1, m) A[i] = Calc(i);
For (i, 1, n - 1) {
g[i] = Comb[n - 1][i];
For (j, 1, m)
g[i] = 1ll * g[i] * Comb[n - 1 - i][r[j] - 1] % Mod * A[j] % Mod;
}
int ans = 0;
For (i, k, n - 1)
ans = (ans + ((i - k) & 1 ? -1ll : 1ll) * Comb[i][k] * g[i]) % Mod;
printf ("%d\n", (ans + Mod) % Mod);
return 0;
}
JLOI2016 简要题解的更多相关文章
- Noip 2014酱油记+简要题解
好吧,day2T1把d默认为1也是醉了,现在只能期待数据弱然后怒卡一等线吧QAQ Day0 第一次下午出发啊真是不错,才2小时左右就到了233,在车上把sao和fate补掉就到了= = 然后到宾馆之后 ...
- Tsinghua 2018 DSA PA2简要题解
反正没时间写,先把简要题解(嘴巴A题)都给他写了记录一下. upd:任务倒是完成了,我也自闭了. CST2018 2-1 Meteorites: 乘法版的石子合并,堆 + 高精度. 写起来有点烦貌似. ...
- Codeforces 863 简要题解
文章目录 A题 B题 C题 D题 E题 F题 G题 传送门 简要题解?因为最后一题太毒不想写了所以其实是部分题解... A题 传送门 题意简述:给你一个数,问你能不能通过加前导000使其成为一个回文数 ...
- HNOI2018简要题解
HNOI2018简要题解 D1T1 寻宝游戏 题意 某大学每年都会有一次 Mystery Hunt 的活动,玩家需要根据设置的线索解谜,找到宝藏的位置,前一年获胜的队伍可以获得这一年出题的机会. 作为 ...
- JXOI2018简要题解
JXOI2018简要题解 T1 排序问题 题意 九条可怜是一个热爱思考的女孩子. 九条可怜最近正在研究各种排序的性质,她发现了一种很有趣的排序方法: Gobo sort ! Gobo sort 的算法 ...
- BJOI2018简要题解
BJOI2018简要题解 D1T1 二进制 题意 pupil 发现对于一个十进制数,无论怎么将其的数字重新排列,均不影响其是不是 \(3\) 的倍数.他想研究对于二进制,是否也有类似的性质. 于是他生 ...
- CQOI2018简要题解
CQOI2018简要题解 D1T1 破解 D-H 协议 题意 Diffie-Hellman 密钥交换协议是一种简单有效的密钥交换方法.它可以让通讯双方在没有事先约定密钥(密码)的情况下,通过不安全的信 ...
- AtCoder ExaWizards 2019 简要题解
AtCoder ExaWizards 2019 简要题解 Tags:题解 link:https://atcoder.jp/contests/exawizards2019 很水的一场ARC啊,随随便便就 ...
- Comet OJ - Contest #2 简要题解
Comet OJ - Contest #2 简要题解 cometoj A 模拟,复杂度是对数级的. code B 易知\(p\in[l,r]\),且最终的利润关于\(p\)的表达式为\(\frac{( ...
随机推荐
- ES优化
1.内存优化 在bin/elasticsearch.in.sh中进行配置 修改配置项为尽量大的内存: 1 2 ES_MIN_MEM=8g ES_MAX_MEM=8g 两者最好改成一样的,否则容易引发长 ...
- html总结:表格中的文字居中
<style> table { text-align:center; } </style>
- Spring Mvc和Spring Boot读取Profile方式
spring boot java代码中获取spring.profiles.active - u013042707的专栏 - CSDN博客https://blog.csdn.net/u013042707 ...
- Android下的软件合集
在平常使用Android手机的时候,选择一个好的软件可以做到事半功倍的效果,所以在此总结一下,加速我们的工作与生活效率 1) ConnectBot ConnectBot是一个Android操作系统上的 ...
- Mac 在terminal 上用命令打开sublime
Step1. 安装Sublime Text编辑器 可直接到以下网址下载dmg安装文件: Sublime Text 3 Step2. 添加命令行别名 打开用户配置文件 vim ~/.bash_profi ...
- Docker安装部署redis
借鉴博客:https://my.oschina.net/u/3489495/blog/1825335 待续... >>>>>>>>>docker安 ...
- C# Note7:MVVM模式之数据绑定
一.资源说明 (1)本文参考自: 一步步走进WPF的MVVM模式(二):数据绑定 WPF之数据绑定总结 二.正文 数据绑定 (Data Binding)是WPF最重要的特性之一,也是实现 MVVM( ...
- Appscanner实验还原code2
import _pickle as pickle from sklearn import svm, ensemble import random from sklearn.metrics import ...
- python数学第二天【泰勒展开式】
1. 泰勒展开式 推论1: 泰勒展开式的应用 推论2: 推论3:
- MyBatis的XML中使用内部类的方式
内部类需要使用$符号连接,而不是点.,如 com.pingan.job.openapi.model.SMSESBResult$ReceiveResult$ResultInfo 从CSDN论坛查到的. ...