HAOI2016 简要题解
「HAOI2016」食物链
题意
现在给你 \(n\) 个物种和 \(m\) 条能量流动关系,求其中的食物链条数。
\(1 \leq n \leq 100000, 0 \leq m \leq 200000\)
题解
拓扑 \(dp\) 入门题,没什么好讲的。
但是注意要看清题,单个生物不算食物链。
代码
#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 ("2060.in", "r", stdin);
freopen ("2060.out", "w", stdout);
#endif
}
const int N = 1e5 + 1e3;
int n, m, f[N];
vector<int> G[N]; int indeg[N];
int main () {
File();
n = read(); m = read();
For (i, 1, m) {
int u = read(), v = read();
G[u].push_back(v); ++ indeg[v];
}
queue<int> Q;
For (i, 1, n) if (!indeg[i] && G[i].size()) Q.push(i), f[i] = 1;
int ans = 0;
while (!Q.empty()) {
int u = Q.front(); Q.pop();
for (int v : G[u]) {
f[v] += f[u];
if (!(-- indeg[v])) Q.push(v);
}
if (!bool(G[u].size())) ans += f[u];
}
printf ("%d\n", ans);
return 0;
}
「HAOI2016」放棋子
题意
给你一个 \(N \times N\) 的矩阵,每行有一个障碍,数据保证任意两个障碍不在同一行,任意两个障碍不在同一列,要求你在这个矩阵上放 \(N\) 枚棋子(障碍的位置不能放棋子),要求你放 \(N\) 个棋子也满足每行只有一枚棋子,每列只有一枚棋子的限制,求有多少种方案。
\(N \le 200\)
题解
题意其实就是给你一个障碍排列 \(\{A_i\}\) 求有多少个排列 \(\{P_i\}\) 满足对于 \(\forall i\) 都有 \(A_i \not = P_i\) 。
不难发现 \(A_i\) 的顺序是不影响答案的,那么就是错排数了。
利用递推公式 \(f[n] = (n - 1)(f[n - 1] + f[n - 2])\) 和 python
的高精度就可以求解了。(偷懒啦)
代码
n = int(input())
f = [0] * (n + 3)
f[2] = 1
for i in range(3, n + 1):
f[i] = (i - 1) * (f[i - 1] + f[i - 2])
print(f[n])
「HAOI2016」地图
题意
出题人语文老师 die 了。
有一个 \(n\) 个点 \(m\) 条边的仙人掌,其中每个点有一种颜色种类 \(a_i\) 。
有 \(q\) 次询问,每次给出三个参数 \(ty, x, y\) ,表示如果当前 \(1 \to x\) 所有简单路径都封死的情况下,\(x\) 能到达点的颜色编号 \(a_i \le y\) 且出现次数 \(\mod 2 = ty\) 的颜色有多少种。。
\(n \leq 100000, m \leq 150000, Q \leq 100000, a_i \leq 10^6\)
题解
前面那个仙人掌,然后封路。其实就是对应求出圆方树后 \(x\) 的子树。(至于这个圆方树不一定要对于点双建新点,用原来的点就行了)
问题就转化成为多次询问区间中颜色在一段区间内的颜色 \(\le y\) 出现次数为奇/偶的点。
如果做过 Gty的二逼妹子序列 那么就绝对会做这题啦。
考虑莫队,那么插入和删除都要 \(\mathcal O(n \sqrt m)\) 次,询问区间权值种类只有 \(\mathcal O(m)\) 次。
如果用线段树维护,瓶颈在于前面插入删除,就变成 \(\mathcal O(n \sqrt m \log n)\) 应该跑不过。
利用平衡结合的思路,减少前者的复杂度,增加后者的复杂度。不难想到分块可以做到 \(\mathcal O(1)- \mathcal O(\sqrt n)\) 或者 \(\mathcal O(\sqrt n) - \mathcal O(1)\) 插入+询问。
那么就可以做到 \(\mathcal O(n \sqrt m + m \sqrt n)\) 啦。
代码
#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 pb push_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 ("2062.in", "r", stdin);
freopen ("2062.out", "w", stdout);
#endif
}
const int N = 1.5e5 + 1e3;
int n, m;
vector<int> G[N], E[N];
int dfn[N], lowlink[N], stk[N], top;
void Tarjan(int u, int fa = 0) {
static int clk = 0;
dfn[u] = lowlink[u] = ++ clk;
stk[++ top] = u;
for (int v : G[u]) if (!dfn[v]) {
Tarjan(v, u);
chkmin(lowlink[u], lowlink[v]);
if (lowlink[v] >= dfn[u]) {
int cur;
do E[u].pb(cur = stk[top --]); while (cur != v);
}
} else if (v != fa) chkmin(lowlink[u], dfn[v]);
}
int efn[N], num[N];
void Dfs_Init(int u, int fa = 0) {
static int clk = 0;
num[dfn[u] = ++ clk] = u;
for (int v : E[u])
if (v != fa) Dfs_Init(v, u);
efn[u] = clk;
}
int a[N], Hash[N];
int blksz, blkid[N], Beg[N], End[N];
struct Query {
int id, opt, l, r, lim;
} Q[N];
struct Cmp {
inline bool operator () (const Query &lhs, const Query &rhs) {
if (blkid[lhs.l] != blkid[rhs.l]) return blkid[lhs.l] < blkid[rhs.l];
if (lhs.r != rhs.r) return lhs.r < rhs.r;
return lhs.id < rhs.id;
}
};
int sum[N][2], times[N];
inline void Insert(int col) {
if (times[col]) -- sum[blkid[col]][times[col] & 1];
++ sum[blkid[col]][(++ times[col]) & 1];
}
inline void Delete(int col) {
-- sum[blkid[col]][times[col] & 1];
if ((-- times[col])) ++ sum[blkid[col]][times[col] & 1];
}
int ans[N];
int main () {
File();
n = read(); m = read();
For (i, 1, n)
Hash[i] = a[i] = read();
sort(Hash + 1, Hash + n + 1);
int cnt = unique(Hash + 1, Hash + n + 1) - Hash - 1;
For (i, 1, n)
a[i] = lower_bound(Hash + 1, Hash + cnt + 1, a[i]) - Hash;
For (i, 1, m) {
int u = read(), v = read();
G[u].pb(v); G[v].pb(u);
}
Tarjan(1); Dfs_Init(1);
int q = read();
blksz = sqrt(max(n, q) + .5);
For (i, 1, max(n, q)) {
blkid[i] = i / blksz + 1;
if (blkid[i] != blkid[i - 1])
End[blkid[i - 1]] = i - 1, Beg[blkid[i]] = i;
}
End[blkid[max(n, q)]] = max(n, q);
For (i, 1, q) {
int opt = read(), x = read(), y = read();
y = upper_bound(Hash + 1, Hash + cnt + 1, y) - Hash - 1;
Q[i] = (Query) {i, opt, dfn[x], efn[x], y};
}
sort(Q + 1, Q + q + 1, Cmp());
int l = 1, r = 0;
For (i, 1, q) {
while (r < Q[i].r) Insert(a[num[++ r]]);
while (l > Q[i].l) Insert(a[num[-- l]]);
while (r > Q[i].r) Delete(a[num[r --]]);
while (l < Q[i].l) Delete(a[num[l ++]]);
int lim = Q[i].lim, res = 0;
For (j, 1, blkid[lim] - 1)
res += sum[j][Q[i].opt];
For (j, Beg[blkid[lim]], lim) if (times[j])
res += (times[j] & 1) == Q[i].opt;
ans[Q[i].id] = res;
}
For (i, 1, q)
printf ("%d\n", ans[i]);
return 0;
}
「HAOI2016」字符合并
题意
有一个长度为 $ n $ 的 $ 01 $ 串,你可以每次将相邻的 $ k $ 个字符合并,得到一个新的字符并获得一定分数。得到的新字符和分数由这 $ k $ 个字符确定。你需要求出你能获得的最大分数。
$ 1 \leq n \leq 300, \ 0 \leq c_i \leq 1, \ w_i \geq 1, \ k \leq 8$
题解
一开始想到 \(dp\) 了,又不会转移。。。最近为啥这么多这种情况啊 TAT 看来细节还是不会写。
看到数据范围不难想到一个 区间+状压 \(dp\) ,令 \(f_{l, r, S}\) 为 \([l, r]\) 最后合并成 \(S\) 这个状态的最优答案。
注意到如果 \(|S| > k\) 一定不优,我们一定会在当前把它合并掉。
那么转移的时候如果 \(|S| = k\) 那么我们就合并就好啦。
其他的话,转移不需要枚举左右都选了很多个的情况,强制左边选 \(|S| - 1\) 个右边选 \(1\) 个就好啦,讨论的情况会少很多QAQ
这样的话,复杂度是 \(\mathcal O(n^3 2^k)\) 的,可能被卡常。
我们再进行一点小小的优化,你会发现每次合并的时候,很多合并对应的方案是相同的。我们在枚举断点那里每次跳 \(k - 1\) 然后合并就好啦 qwq
复杂度此时就变成 \(\mathcal O(\displaystyle \frac{n^32^k}k)\) 啦,跑的还挺快的。
总结
最优化转移还是那句话,宜多不宜少。你把很多个更优秀的状态记到劣一点的状态上是不会影响答案的,此时转移会变得容易许多。
然后想一个看起来不对的 \(dp\) ,猜测一下它能包含所有状态,举一下反例,发举不出,那么此时它就能包含所有可能的状态啦。
代码
#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;
using ll = long long;
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 ("2063.in", "r", stdin);
freopen ("2063.out", "w", stdout);
#endif
}
const int N = 310;
const ll inf = 0x3f3f3f3f3f3f3f3f;
ll f[N][N][1 << 8], g[2];
char str[N]; int a[N], c[N], w[N];
int main () {
File();
int n = read(), k = read();
scanf ("%s", str + 1);
For (i, 1, n) a[i] = str[i] ^ 48;
Rep (i, 1 << k) c[i] = read(), w[i] = read();
For (i, 1, n) For (j, i, n) Rep (S, 1 << k) f[i][j][S] = -inf;
Fordown (i, n, 1) For (j, i, n) {
if (i == j) {
f[i][j][a[i]] = 0; continue;
}
int len = (j - i) % (k - 1); if (!len) len = k - 1;
for (int mid = j; mid > i; mid -= k - 1) Rep (S, 1 << len) {
chkmax(f[i][j][S << 1], f[i][mid - 1][S] + f[mid][j][0]);
chkmax(f[i][j][S << 1 | 1], f[i][mid - 1][S] + f[mid][j][1]);
}
if (len == k - 1) {
g[0] = g[1] = -inf;
Rep (S, 1 << k)
chkmax(g[c[S]], f[i][j][S] + w[S]);
Rep (id, 2) f[i][j][id] = g[id];
}
}
ll ans = -inf;
Rep (S, 1 << k)
chkmax(ans, f[1][n][S]);
printf ("%lld\n", ans);
return 0;
}
「HAOI2016」找相同字符
可以参考我原来写的 后缀数组小结 ,里面的最后一道例题就是啦。
这个也可以广义 \(SAM\) 解决,或者一个串在另外一个 \(SAM\) 跑匹配就行了。
HAOI2016 简要题解的更多相关文章
- 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{( ...
随机推荐
- 【学习总结】C-翁恺老师-入门-总
2019-1-2 翁恺老师C入门视频-启程 代码详见GitHub: 目录 第0周:程序设计与C语言 第1周:计算 第2周:判断 第3周:循环 第4周:循环控制 第5周:数据类型 第6周:函数 第7周: ...
- 【问题解决方案】Git bash进入多层子目录问题(通配符问题留坑)
cd进入指定路径下:cd 斜杠 斜杠 方法一: 1- 撇丿,不是"那",盘符前面要加上 / (d盘前面也加,不加也行) 2- 路径名不区分大小写 3- 不用空格 4- 如果目录名中 ...
- jmeter高并发设计方案(转)
高并发设计方案二(秒杀架构) 优化方向: (1)将请求尽量拦截在系统上游(不要让锁冲突落到数据库上去).传统秒杀系统之所以挂,请求都压倒了后端数据层,数据读写锁冲突严重,并发高响应慢,几乎所有请求都超 ...
- Oracle 表空间不足引起的问题及解决方法
-- 1 向数据库导入数据时报了ORA-01653: unable to extend table错误,网上查了下原因是由于表空间不足引起的: 查询表空间使用情况语句 select a.tablesp ...
- spark、standalone集群 (1)
1.配置 spark/apache/org 下载解压, 安装jdk1.8 2.准备服务器 3.设置hostname 4.关闭防火墙 开启: service iptables start 关闭: ser ...
- linux安装ssh服务
1.安装openssh-server sudo apt-get install openssh-server 2.检查openssh-server是否安装成功 sudo ps -e | grep ss ...
- Prime Permutation
Prime Permutation 原题地址: http://codeforces.com/problemset/problem/123/A 题目大意: 给你一个字符串(只包含小写字母),从1开始存放 ...
- php foreach跳出本次/当前循环与终止循环方法
continue:跳出本次循环 break:终止循环 exit:用来结束程序执行 return: 用来结束一段代码 $arr= array('le','yang','jun','lecode' ...
- vue上传图片到服务器
https://blog.csdn.net/qq_29712995/article/details/78839093(copy) HTML代码: <input accept="imag ...
- css3特殊图形(气泡)
一.气泡 效果: body{ background: #dd5e9d; height: 100%; } .paopao { position: absolute; width: 200px; heig ...