NOIP2018 解题笔记
D1T1 铺设道路
在场上并没有想到积木大赛这道原题。
差分之后可以把在$[l, r]$这段区间$ - 1$变成在$l$处$ - 1$,在$r + 1$处$ + 1$,然后最终目标是使$\forall i \in [1, n] \ \Delta d_i == 0$成立。就想着把正负数配一配对,然后输出了正数绝对值和负数绝对值的$max$,这导致了我的代码非常鬼畜。
出来之后发现正数绝对值一定大于等于负数绝对值,要不然就会出现$d_i < 0$的情况。
时间复杂度$O(n)$。
#include <cstdio>
#include <cstring>
using namespace std;
typedef long long ll; const int N = 1e5 + ; int n;
ll a[N], b[N]; template <typename T>
inline void read(T &X) {
X = ; char ch = ; T op = ;
for(; ch > '' || ch < ''; ch = getchar())
if(ch == '-') op = -;
for(; ch >= '' && ch <= ''; ch = getchar())
X = (X << ) + (X << ) + ch - ;
X *= op;
} ll abs(ll x) {
return x > ? x : -x;
} inline ll max(ll x, ll y) {
return x > y ? x : y;
} int main() {
// freopen("road.in", "r", stdin);
// freopen("road.out", "w", stdout); read(n);
for(int i = ; i <= n; i++) {
read(a[i]);
b[i] = a[i] - a[i - ];
} /* for(int i = 1; i <= n; i++)
printf("%lld ", b[i]);
printf("\n"); */ ll ps = 0LL, ne = 0LL;
for(int i = ; i <= n; i++) {
if(b[i] > ) ps += b[i];
else ne -= b[i];
} printf("%lld\n", max(ne, ps));
return ;
}
road
D1T2 货币系统
一开始觉得是一道数学题,后来回过头来发现应该可以完全背包,先写了$80$分的暴力,然后又想了一下可以用分治优化到$O(Tnlogna_i)$,就写了一下然后拍了拍,最后在少爷机上$AC$了。
用$solve(l, r)$表示不考虑$[l, r]$这段区间内的货币的情况,然后在向下算的时候每一次做一半就好了。
出来之后发现就是个$sb$排序。
时间复杂度$O(Tna_i)$,放上分治的代码。
#include <cstdio>
#include <cstring>
using namespace std; const int N = ;
const int M = ;
const int Lg = ; int testCase, n, mx = , a[N], ans;
bool f[Lg][M]; inline void read(int &X) {
X = ; char ch = ; int op = ;
for(; ch > '' || ch < ''; ch = getchar())
if(ch == '-') op = -;
for(; ch >= '' && ch <= ''; ch = getchar())
X = (X << ) + (X << ) + ch - ;
X *= op;
} inline void chkMax(int &x, int y) {
if(y > x) x = y;
} void solve(int l, int r, int dep) {
if(l == r) {
if(f[dep][a[l]]) ans--;
return;
} ++dep;
for(int i = ; i <= mx; i++) f[dep][i] = f[dep - ][i]; int mid = ((l + r) >> );
for(int i = l; i <= mid; i++)
for(int j = a[i]; j <= mx; j++)
f[dep][j] |= f[dep][j - a[i]];
solve(mid + , r, dep); for(int i = ; i <= mx; i++) f[dep][i] = f[dep - ][i];
for(int i = mid + ; i <= r; i++)
for(int j = a[i]; j <= mx; j++)
f[dep][j] |= f[dep][j - a[i]];
solve(l, mid, dep);
} int main() {
// freopen("money.in", "r", stdin);
// freopen("money.out", "w", stdout); for(read(testCase); testCase--; ) {
read(n);
mx = ;
for(int i = ; i <= n; i++) {
read(a[i]);
chkMax(mx, a[i]);
} for(int i = ; i <= mx; i++) f[][i] = ;
f[][] = ; ans = n;
solve(, n, ); printf("%d\n", ans);
}
return ;
}
money
D1T3 赛道修建
挺简单的第三题。首先外层二分,然后把一个点的所有儿子存下来,贪心从小到大匹配,在保证对答案的贡献最大的情况下使延伸到父亲处理的链尽量长。我在考场上写了一个常数巨大的$multiset$,也能跑过。
其实也可以再二分一个能向上的最长链然后看看答案会不会变差就行了,这样子常数比较优秀。
仍然不会更高级的解法。
时间复杂度$O(nlog^2n)$。
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <set>
using namespace std; const int N = 5e4 + ;
const int inf = << ; int n, m, cnt, lim, tot = , head[N], f[N];
multiset <int> s[N]; struct Edge {
int to, nxt, val;
} e[N << ]; inline void add(int from, int to, int val) {
e[++tot].to = to;
e[tot].val = val;
e[tot].nxt = head[from];
head[from] = tot;
} inline void read(int &X) {
X = ; char ch = ; int op = ;
for(; ch > '' || ch < ''; ch = getchar())
if(ch == '-') op = -;
for(; ch >= '' && ch <= ''; ch = getchar())
X = (X << ) + (X << ) + ch - ;
X *= op;
} inline void chkMax(int &x, int y) {
if(y > x) x = y;
} /* inline int getPos(int which, int siz, int val) {
val = lim - val;
int ln = 0, rn = siz, mid, res = -1;
for(; ln <= rn; ) {
mid = ((ln + rn) >> 1);
if(vec[which][mid] >= val) res = mid, rn = mid - 1;
else ln = mid + 1;
}
return res;
} */ /* void dfs(int x, int fat) {
vec[x].clear();
for(int i = head[x]; i; i = e[i].nxt) {
int y = e[i].to;
if(y == fat) continue;
dfs(y, x);
vec[x].push_back(f[y] + e[i].val);
} sort(vec[x].begin(), vec[x].end());
int vecSiz = vec[x].size() - 1;
for(; vecSiz; --vecSiz) {
if(vecSiz == -1) break;
if(vec[x][vecSiz] < lim) break;
++cnt;
}
for(int i = 0; i <= vecSiz; i++) tag[i] = 0;
for(int i = vecSiz; i > 0; i--) {
int pos = getPos(x, i - 1, vec[x][i]);
if(pos != -1) tag[pos] = 1, tag[i] = 1, ++cnt;
} int res = 0;
for(int i = 0; i <= vecSiz; i++)
if(tag[i]) tag[i] = 0;
else chkMax(res, vec[x][i]); f[x] = res;
} */ void dfs(int x, int fat) {
// s[x].clear();
for(int i = head[x]; i; i = e[i].nxt) {
int y = e[i].to;
if(y == fat) continue;
dfs(y, x);
s[x].insert(f[y] + e[i].val);
} for(; !s[x].empty(); ) {
multiset <int> :: iterator it = (--s[x].end());
if((*it) >= lim) {
++cnt;
s[x].erase(it);
} else break;
} int res = ;
for(; !s[x].empty(); ) {
multiset <int> :: iterator p1 = s[x].begin();
int tmp = (*p1);
s[x].erase(p1);
multiset <int> :: iterator p2 = s[x].lower_bound(lim - tmp); if(p2 == s[x].end()) {
chkMax(res, tmp);
} else {
cnt++;
s[x].erase(p2);
}
} f[x] = res;
} inline bool chk(int mid) {
lim = mid, cnt = ;
for(int i = ; i <= n; i++) f[i] = ;
dfs(, );
return cnt >= m;
} int main() {
// freopen("track.in", "r", stdin);
// freopen("track.out", "w", stdout); read(n), read(m);
int ln = , rn = , mid, res = inf;
for(int x, y, v, i = ; i < n; i++) {
read(x), read(y), read(v);
add(x, y, v), add(y, x, v);
rn += v;
} for(; ln <= rn; ) {
mid = (ln + rn) / ;
if(chk(mid)) ln = mid + , res = mid;
else rn = mid - ;
} printf("%d\n", res);
return ;
}
track
D2T1 旅行
原来以为图论跑到$T1$来了,没想到最后还是个树。
先考虑树的部分分,发现只有走完一个点的所有子树之后才能向上走,所以每次贪心地走最小的子树就好了;然后考虑基环树的部分分,看到$n$不超过$5000$,直接把环找出来然后断一断走一走取个最小就好了。
场上写出了一个$bug$但只被卡了$4$分。
树的话时间复杂度是$O(nlogn)$,基环树是$O(n^2)$。
#include <cstdio>
#include <cstring>
#include <vector>
#include <algorithm>
using namespace std; const int N = ;
const int inf = << ; int n, m, cnt = , ans[N];
vector <int> e[N]; inline void read(int &X) {
X = ; char ch = ; int op = ;
for(; ch > '' || ch < ''; ch = getchar())
if(ch == '-') op = -;
for(; ch >= '' && ch <= ''; ch = getchar())
X = (X << ) + (X << ) + ch - ;
X *= op;
} namespace Solve1 { void dfs(int x, int fat) {
int vecSiz = e[x].size();
for(int i = ; i < vecSiz; i++) {
int y = e[x][i];
if(y == fat) continue;
ans[++cnt] = y;
dfs(y, x);
}
} void work() {
ans[++cnt] = ;
dfs(, ); for(int i = ; i <= n; i++) {
printf("%d", ans[i]);
if(i == n) putchar('\n');
else putchar(' ');
}
} } namespace Solve2 {
int top, stk[N], sum, cir[N], dx, dy, res[N];
bool inc[N], vis[N], flag = ; void getCir(int x, int fat) {
// int pre = top;
if(flag) return;
stk[++top] = x, vis[x] = ;
int vecSiz = e[x].size();
for(int i = ; i < vecSiz; i++) {
int y = e[x][i];
if(y == fat) continue;
if(vis[y]) {
flag = ;
sum = ;
for(; stk[top] != y; --top) {
inc[stk[top]] = ;
vis[stk[top]] = ;
cir[++sum] = stk[top];
}
inc[y] = , cir[++sum] = y, vis[y] = , top--;
return;
} else getCir(y, x);
}
// top = pre;
if(vis[x]) --top, vis[x] = ;
} inline bool bet() {
for(int i = ; i <= n; i++)
if(res[i] != ans[i]) return res[i] < ans[i];
return ;
} void dfs(int x, int fat) {
int vecSiz = e[x].size();
for(int i = ; i < vecSiz; i++) {
int y = e[x][i];
if(y == fat) continue;
if((x == dx && y == dy) || (x == dy && y == dx)) continue;
res[++cnt] = y;
dfs(y, x);
}
} void work() {
top = ;
getCir(, ); /* for(int i = 1; i <= sum; i++)
printf("%d ", cir[i]);
printf("\n"); */ for(int i = ; i <= n; i++) ans[i] = inf; for(int i = ; i < sum; i++) {
dx = cir[i], dy = cir[i + ];
res[cnt = ] = ;
dfs(, );
if(bet()) {
for(int j = ; j <= n; j++)
ans[j] = res[j];
}
}
dx = cir[], dy = cir[sum];
res[cnt = ] = ;
dfs(, );
if(bet()) {
for(int j = ; j <= n; j++)
ans[j] = res[j];
} for(int i = ; i <= n; i++) {
printf("%d", ans[i]);
if(i == n) putchar('\n');
else putchar(' ');
}
} } int main() {
// freopen("travel.in", "r", stdin);
// freopen("travel.out", "w", stdout); // freopen("testdata.in", "r", stdin); read(n), read(m);
for(int x, y, i = ; i <= m; i++) {
read(x), read(y);
e[x].push_back(y), e[y].push_back(x);
} for(int i = ; i <= n; i++)
sort(e[i].begin(), e[i].end()); if(m == n - ) Solve1 :: work();
else Solve2 :: work(); return ;
}
travel
D2T2 填数游戏
我到现在都还认为这是一道打表题。
放上大神的状压题解 戳这里
首先写个暴力写打出$(n, n)$的表$(n \leq 8)$,然后就靠这三条性质求答案:
1、$(n, m) = (m, n)$。
2、$(n, m) = (n, m - 1) * 3$ $(m > n)$。
3、$$ (n, n + 1) = \left\{\begin{matrix}
(n, n) * 3 & (n \leq 3) \\
(n, n) * 3 - 2^n * 3& (n \geq 4)
\end{matrix}\right. $$
时间复杂度$O(logn)$。
#include <cstdio>
#include <cstring>
using namespace std;
typedef long long ll; const int N = ;
const ll P = 1e9 + ;
const ll base[N] = {, , , , , , , , , }; int n, m; template <typename T>
inline void swap(T &x, T &y) {
T t = x; x = y; y = t;
} inline ll fpow(ll x, ll y) {
ll res = 1LL;
for(; y > ; y >>= ) {
if(y & ) res = res * x % P;
x = x * x % P;
}
return res;
} int main() {
scanf("%d%d", &n, &m);
if(n > m) swap(n, m);
if(n == ) return printf("%lld\n", fpow(2LL, m)), ;
if(n == m) return printf("%lld\n", base[n]), ;
ll ans = 1LL;
if(n > ) ans = (3LL * base[n] % P - 3LL * fpow(2LL, n) % P + P) % P;
else ans = base[n] * 3LL % P;
ans = ans * fpow(3LL, m - n - ) % P;
printf("%lld\n", ans);
return ;
}
game
D2T3 保卫王国
为什么会有动态$dp$这种东西出现啊啊啊啊啊啊。
我还是只会倍增的做法。用$h_{x, 0/1}$表示$x$的子树中选/不选$x$的最小代价,用$g_{x, 0/1}$表示$x$到根的链上不算$x$的子树$x$选/不选的最小代价,用$f_{x, i, 0/1, 0/1}$表示从$x$向上跳$2^i$的这条链上不算$x$的子树其他子树的最小代价。
然后剩下看代码吧,感觉代码肯定比我讲得清楚。
时间复杂度$O(nlogn)$,常数巨大。
#include <cstdio>
#include <cstring>
#include <map>
#include <algorithm>
using namespace std;
typedef long long ll;
typedef pair <int, int> pin; const int N = 1e5 + ;
const int Lg = ;
const ll inf = 1LL << ; int n, qn, tot = , head[N], fa[N][Lg], dep[N];
ll a[N], h[N][], f[N][Lg][][], g[N][];
map <pin, int> ex; struct Edge {
int to, nxt;
} e[N << ]; inline void add(int from, int to) {
e[++tot].to = to;
e[tot].nxt = head[from];
head[from] = tot;
} template <typename T>
inline void read(T &X) {
X = ; char ch = ; T op = ;
for(; ch > '' || ch < ''; ch = getchar())
if(ch == '-') op = -;
for(; ch >= '' && ch <= ''; ch = getchar())
X = (X << ) + (X << ) + ch - ;
X *= op;
} template <typename T>
inline void chkMin(T &x, T y) {
if(y < x) x = y;
} inline ll min(ll x, ll y) {
return x > y ? y : x;
} void dp1(int x, int fat) {
h[x][] = , h[x][] = a[x];
for(int i = head[x]; i; i = e[i].nxt) {
int y = e[i].to;
if(y == fat) continue;
dp1(y, x);
h[x][] += h[y][];
h[x][] += min(h[y][], h[y][]);
}
} void dp2(int x, int fat) {
for(int i = head[x]; i; i = e[i].nxt) {
int y = e[i].to;
if(y == fat) continue;
g[y][] = g[x][] + h[x][] - min(h[y][], h[y][]);
g[y][] = min(g[y][], h[x][] - h[y][] + g[x][]);
dp2(y, x);
}
} void dfs(int x, int fat, int depth) {
fa[x][] = fat, dep[x] = depth; f[x][][][] = inf, f[x][][][] = h[fat][] - min(h[x][], h[x][]);
f[x][][][] = h[fat][] - h[x][], f[x][][][] = h[fat][] - min(h[x][], h[x][]);
for(int i = ; i <= ; i++) {
fa[x][i] = fa[fa[x][i - ]][i - ];
for(int u = ; u < ; u++)
for(int v = ; v < ; v++) {
f[x][i][u][v] = inf;
for(int k = ; k < ; k++)
chkMin(f[x][i][u][v], f[x][i - ][u][k] + f[fa[x][i - ]][i - ][k][v]);
}
} for(int i = head[x]; i; i = e[i].nxt) {
int y = e[i].to;
if(y == fat) continue;
dfs(y, x, depth + );
}
} inline void solve(int x, int tx, int y, int ty) {
if(dep[x] < dep[y])
swap(x, y), swap(tx, ty); ll resx[] = {inf, inf}, resy[] = {inf, inf}, tox[], toy[];
resx[tx] = h[x][tx], resy[ty] = h[y][ty]; for(int i = ; i >= ; i--)
if(dep[fa[x][i]] >= dep[y]) {
tox[] = tox[] = inf;
for(int u = ; u < ; u++)
for(int v = ; v < ; v++)
chkMin(tox[u], resx[v] + f[x][i][v][u]);
resx[] = tox[], resx[] = tox[];
x = fa[x][i];
} if(x == y) {
printf("%lld\n", resx[ty] + g[y][ty]);
return;
} for(int i = ; i >= ; i--)
if(fa[x][i] != fa[y][i]) {
tox[] = tox[] = inf;
for(int u = ; u < ; u++)
for(int v = ; v < ; v++)
chkMin(tox[u], resx[v] + f[x][i][v][u]);
resx[] = tox[], resx[] = tox[]; toy[] = toy[] = inf;
for(int u = ; u < ; u++)
for(int v = ; v < ; v++)
chkMin(toy[u], resy[v] + f[y][i][v][u]);
resy[] = toy[], resy[] = toy[]; x = fa[x][i], y = fa[y][i];
} int z = fa[x][];
ll res = h[z][] - h[x][] - h[y][] + g[z][] + resx[] + resy[];
chkMin(res, h[z][] - min(h[x][], h[x][]) - min(h[y][], h[y][]) +
g[z][] + min(resx[], resx[]) + min(resy[], resy[]));
printf("%lld\n", res);
} int main() {
read(n), read(qn);
char typ[]; scanf("%s", typ);
for(int i = ; i <= n; i++) read(a[i]);
for(int x, y, i = ; i < n; i++) {
read(x), read(y);
add(x, y), add(y, x);
ex[pin(x, y)] = ex[pin(y, x)] = ;
} dp1(, ), dp2(, ), dfs(, , ); for(int x, tx, y, ty; qn--; ) {
read(x), read(tx), read(y), read(ty);
if(ex.find(pin(x, y)) != ex.end() && !tx && !ty) puts("-1");
else solve(x, tx, y, ty);
} return ;
}
defense
NOIP2018 解题笔记的更多相关文章
- 《剑指offer》解题笔记
<剑指offer>解题笔记 <剑指offer>共50题,这两周使用C++花时间做了一遍,谨在此把一些非常巧妙的方法.写代码遇到的难点.易犯错的细节等做一个简单的标注,但不会太过 ...
- 122. Best Time to Buy and Sell Stock(二) leetcode解题笔记
122. Best Time to Buy and Sell Stock II Say you have an array for which the ith element is the price ...
- 110.Balanced Binary Tree Leetcode解题笔记
110.Balanced Binary Tree Given a binary tree, determine if it is height-balanced. For this problem, ...
- 2016/9/21 leetcode 解题笔记 395.Longest Substring with At Least K Repeating Characters
Find the length of the longest substring T of a given string (consists of lowercase letters only) su ...
- LeetCode解题笔记 - 3. Longest Substring Without Repeating Characters
Given a string, find the length of the longest substring without repeating characters. Examples: Giv ...
- LeetCode解题笔记 - 2. Add Two Numbers
2. Add Two Numbers You are given two non-empty linked lists representing two non-negative integers. ...
- 解题笔记——NIT 遥远的村庄
某个小镇有 N 个村庄,村庄编号1-N,给出 M 条单向道路,不存在环,即不存在 村庄A可以到达村庄B 且 村庄B也可以到达村庄A的情况.如果村庄A与村庄B之间存在一条单向道路,则说村庄A和村庄B之间 ...
- HDU-5902-GCD is Funny解题笔记
Alex has invented a new game for fun. There are n integers at a board and he performs the following ...
- CTF实验吧-WEB题目解题笔记(1)简单的登陆题
1.简单的登陆题 解题链接: http://ctf5.shiyanbar.com/web/jiandan/index.php Burp抓包解密 乱码,更换思路.尝试id intruder 似乎也没什 ...
随机推荐
- CODEVS1049 棋盘染色
题目大意:01矩阵,1表示黑色,0表示白色,求将白色染成黑色最少的次数 使黑色成为一整个联通块. 题解: 搜索bfs 90... dfs判断连通 #include<iostream> #i ...
- 2.2 web工程的目录结构
[转] 一个最简单的Web应用的目录结构如下所示: Web应用的结构定义在Servlet的规范中,目前最新版本为3.1. 下载地址:https://jcp.org/aboutJava/communit ...
- centos 7 bbr 安装
1. 准备 升级内核需要4.9 以及以上 2. yum 内核升级 a. 添加 ELRepo 源 GPG key rpm --import https://www.elrepo.or ...
- Oracle视图编译错误解决办法
因为新搭的环境,数据库是从另一个现成的环境导过来的,直接后台用exp和imp命令操作.但是新环境的Oracle数据库有问题,一些视图创建不了,导致用到这些视图的视图和存储过程也编译不了.后来手工重新编 ...
- .Net上传文件超过了最大请求长度
错误消息:超过了最大请求长度 错误原因:asp.net默认最大上传文件大小为4M,运行超时时间为90S. 修改web.config文件可以改变这个默认值为 <configuration&g ...
- 1112 Stucked Keyboard
题意:坏掉的键若被按下,总是重复打出k次.比如,k为3,打出的序列如下—— thiiis iiisss a teeeeeest 坏掉的键是i和e,虽然iiisss中s也出现了3次,但它不是坏掉的键,因 ...
- nandflash裸机程序分析
它包含7个文件: head.S init.c main.c Makefile nand.c nand.lds 我们之前的程序都是在nandflash的前4k放代码,上电后自动拷贝到SRAM中,之后将S ...
- js中的Math
js中的Math Math.round 取最接近的整数 Math.round(-2.7) // -3 Math.ceil 向上取整 Math.ceil(1.1) // 2 Math.floor 向下取 ...
- 微信小程序简单步骤记录
- VS2010调用halcon的时候出现试图加载格式不正确的程序(this.hWindowControl1 = new HalconDotNet.HWindowControl();)
[重要错误修改] /// <summary> /// 设计器支持所需的方法 - 不要 /// 使用代码编辑器修改此方法的内容. /// </summary> private v ...