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. mysql安装问题汇总

    操作系统:win7 mysql版本号:mysql 5.5(64位) 1.又一次安装mysql卡在最后一步过不去 问题描写叙述:第一次安装完mysql.使用jdbc写入的中文都是乱码.但已存在于数据库中 ...

  2. Oracle事务的隔离

    事务是指一些列操作的集合,它有4个属性:原子性(Automacity).一致性(Consistency).隔离性(Isolation)和持久性(Durability),这4个属性简称为ACID.原子性 ...

  3. ORACLE常识

    1. ORACLE中查看表中的外键来源于哪些表 select cl.table_name from user_cons_columns cl left join user_constraints c ...

  4. Velodyne线性激光雷达pcap文件格式及写入、数据解析 Lebal:激光雷达

    转载自https://blog.csdn.net/qq_25241325/article/details/80766305 roslaunch loam_velodyne loam_velodyne. ...

  5. 杂项:WiKi

    ylbtech-杂项:WiKi Wiki是一种在网络上开放且可供多人协同创作的超文本系统,由沃德·坎宁安于1995年首先开发,这种超文本系统支持面向社群的协作式写作,同时也包括一组支持这种写作.沃德· ...

  6. Mongodb时间问题

    Java保存到mongodb当前时间,使用RoboMongo查看数据显示时间比当前时间少8个小时,这是客户端的问题. MongoDB中的Date类型数据只保存绝对时间值,不保存时区信息,因此“显示的时 ...

  7. PHP下的命令行执行 php -S localhost -t public(public是根目录,也是入口文件所在目录,是LARAVEL的)

    PHP 的命令行模式     以下是 PHP 二进制文件(即 php.exe 程序)提供的命令行模式的选项参数,您随时可以通过 PHP -h 命令来查询这些参数. Usage: php [option ...

  8. 32位机,CPU是如何利用段寄存器寻址的

    转自:http://blog.sina.com.cn/s/blog_640531380100xa15.html 32位cpu 地址线扩展成了32位,这和数据线的宽度是一致的.因此,在32位机里其实并不 ...

  9. GitHub in vs2010、vs2013

    GitHub在使用上大致和其他源代码管理工具一样,个人源代码管理和分享一大利器,而且vs2010和vs2013配置也没有任何区别,简单做了一下图文配置说明 一.注册github 1.github.co ...

  10. 第十八章 MySQL Workbench5.2使用(待续)

    ··········