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 解题笔记的更多相关文章

  1. 《剑指offer》解题笔记

    <剑指offer>解题笔记 <剑指offer>共50题,这两周使用C++花时间做了一遍,谨在此把一些非常巧妙的方法.写代码遇到的难点.易犯错的细节等做一个简单的标注,但不会太过 ...

  2. 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 ...

  3. 110.Balanced Binary Tree Leetcode解题笔记

    110.Balanced Binary Tree Given a binary tree, determine if it is height-balanced. For this problem, ...

  4. 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 ...

  5. LeetCode解题笔记 - 3. Longest Substring Without Repeating Characters

    Given a string, find the length of the longest substring without repeating characters. Examples: Giv ...

  6. LeetCode解题笔记 - 2. Add Two Numbers

    2. Add Two Numbers You are given two non-empty linked lists representing two non-negative integers. ...

  7. 解题笔记——NIT 遥远的村庄

    某个小镇有 N 个村庄,村庄编号1-N,给出 M 条单向道路,不存在环,即不存在 村庄A可以到达村庄B 且 村庄B也可以到达村庄A的情况.如果村庄A与村庄B之间存在一条单向道路,则说村庄A和村庄B之间 ...

  8. 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 ...

  9. CTF实验吧-WEB题目解题笔记(1)简单的登陆题

    1.简单的登陆题 解题链接: http://ctf5.shiyanbar.com/web/jiandan/index.php  Burp抓包解密 乱码,更换思路.尝试id intruder 似乎也没什 ...

随机推荐

  1. python笔记-4(装饰器、生成器、迭代器)

    一.熟练掌握装饰器的原理 (在装饰器学习的过程中,查了看了很多资料,个人感觉走了很多的弯路,这个笔记,分享我的理解,希望能帮助到一些人.本文对装饰器的描述,侧重点是条理与逻辑思路,想通过从无到有的方式 ...

  2. Java实现7种常见的排序算法

    数据结构中的内部排序:不需要访问外存便能完成,是一个逐步扩大记录的有序序列长度的过程. 可以分为5类: 1.插入排序:直接插入排序,稳定排序,时间复杂度为O(n^2)非递减有序,设置r[0]为哨兵进行 ...

  3. C# 数组、ArrayList和List三者的区别

    在C#中数组,ArrayList,List都能够存储一组对象,那么这三者到底有什么样的区别呢. 数组 数组在C#中最早出现的.在内存中是连续存储的,所以它的索引速度非常快,而且赋值与修改元素也很简单. ...

  4. NPOI时间格式判断

    switch (cell.CellType) { case CellType.BLANK: //空数据类型处理 dr[iRow] = ""; break; case CellTyp ...

  5. 一周学会HTML----Day03常用标签(下)

    form 标签 表单(重要) 表单用途 用于收集用户信息,提交给服务器 基本使用 (action,method,enctype) 要提交的表单必须放到form里 input select textar ...

  6. FPGA前世今生(四)

    前几期我们一直介绍FPGA内部资源,今天我们将用最后的篇幅来介绍剩下的内部资源部分,结束我们FPGA的前世今生.之所以起名字为FPGA前世今生,其实就是介绍一下FPGA内部资源,前世的内部结构资源就不 ...

  7. 蓝桥杯 算法训练 ALGO-50 数组查找及替换

    算法训练 数组查找及替换   时间限制:1.0s   内存限制:512.0MB 问题描述 给定某整数数组和某一整数b.要求删除数组中可以被b整除的所有元素,同时将该数组各元素按从小到大排序.如果数组元 ...

  8. Oracle日常性能问题查看

    1 判断回滚段竞争的sql --当Ratio大于2时存在回滚段竞争,需要增加更多的回滚段) ratio from v$rollstat rs, v$rollname rn where rs.USN = ...

  9. from表单

    构建一个表单 假设你想在你的网站上创建一个简单的表单,以获得用户的名字.你需要类似这样的模板: 1 2 3 4 5 <form action="/your-name/" me ...

  10. 参数传递中编码问题(Get/Post 方式)(三)

    自己总结的:     GET方式:      1.提交方式为GET时,数据是放在请求HEADER头中的,且将数据与URL拼接后,浏览器会对拼接后的url进行编码,编码方式为浏览器默认的编码,      ...